mirror of
https://github.com/theupriser/steamify-cachyos.git
synced 2026-10-03 17:41:58 +02:00
Merge pull request #25 from theupriser/feat/fremont-poweroff
Feat/fremont poweroff
This commit is contained in:
commit
956b712099
20 files changed
+500
-162
No files matched your search
@@ -31,6 +31,23 @@ version="v$(grep -oP '^VERSION=\K.*' "$entry"), $(git describe --always --dirty
|
|||||||
sed -e '1{/^#!/d}' -e '/^# Sourced by steamify.sh/d' "lib/$lib.sh"
|
sed -e '1{/^#!/d}' -e '/^# Sourced by steamify.sh/d' "lib/$lib.sh"
|
||||||
echo
|
echo
|
||||||
done
|
done
|
||||||
|
# patches/ embedded: replaces lib/common.sh's patch_file, which reads
|
||||||
|
# them from the checkout.
|
||||||
|
echo "# ----- patches/ -----"
|
||||||
|
echo "patch_file() {"
|
||||||
|
echo ' case "$1" in'
|
||||||
|
for f in patches/*; do
|
||||||
|
[[ "$f" == patches/README.md ]] && continue
|
||||||
|
grep -q '^STEAMIFY_PATCH_EOF$' "$f" && { echo "bundle: $f contains the heredoc delimiter" >&2; exit 1; }
|
||||||
|
echo " $(basename "$f")) cat << 'STEAMIFY_PATCH_EOF'"
|
||||||
|
cat "$f"
|
||||||
|
echo "STEAMIFY_PATCH_EOF"
|
||||||
|
echo " ;;"
|
||||||
|
done
|
||||||
|
echo ' *) return 1 ;;'
|
||||||
|
echo ' esac'
|
||||||
|
echo "}"
|
||||||
|
echo
|
||||||
echo "# ----- steamify.sh -----"
|
echo "# ----- steamify.sh -----"
|
||||||
echo "main() {"
|
echo "main() {"
|
||||||
# Everything after the source loop is the entry point's body.
|
# Everything after the source loop is the entry point's body.
|
||||||
|
|||||||
@@ -6,7 +6,7 @@
|
|||||||
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash
|
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify.sh | bash
|
||||||
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/download/latest/steamify.sh | bash
|
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/download/latest/steamify.sh | bash
|
||||||
# Each release also has the app (v2): steamify-app.tar.gz (ui/, steamify.sh,
|
# Each release also has the app (v2): steamify-app.tar.gz (ui/, steamify.sh,
|
||||||
# lib/, the icon) and its launcher, run with:
|
# lib/, patches/, the icon) and its launcher, run with:
|
||||||
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash
|
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash
|
||||||
# A push to feat/v2-ui (the app, not released yet) updates the pre-release
|
# A push to feat/v2-ui (the app, not released yet) updates the pre-release
|
||||||
# v2-ui-preview instead, whose launcher gets that build:
|
# v2-ui-preview instead, whose launcher gets that build:
|
||||||
@@ -36,7 +36,7 @@ jobs:
|
|||||||
.github/tools/bundle.sh dist/steamify.sh
|
.github/tools/bundle.sh dist/steamify.sh
|
||||||
# The app: sources, not the single-file build (its backend mode runs
|
# The app: sources, not the single-file build (its backend mode runs
|
||||||
# steamify.sh from its own folder).
|
# steamify.sh from its own folder).
|
||||||
tar -czf dist/steamify-app.tar.gz steamify.sh lib ui assets/steam-gaming-settings.svg LICENSE.md
|
tar -czf dist/steamify-app.tar.gz steamify.sh lib patches ui assets/steam-gaming-settings.svg LICENSE.md
|
||||||
cp ui/steamify-app.sh dist/steamify-app.sh
|
cp ui/steamify-app.sh dist/steamify-app.sh
|
||||||
|
|
||||||
- name: Check
|
- name: Check
|
||||||
|
|||||||
@@ -15,6 +15,10 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
|
|||||||
- `steamify.sh` - the only entry point. Pre-flight checks,
|
- `steamify.sh` - the only entry point. Pre-flight checks,
|
||||||
sources `lib/*.sh`, runs the menu and applies the plan. No component
|
sources `lib/*.sh`, runs the menu and applies the plan. No component
|
||||||
logic here.
|
logic here.
|
||||||
|
- `patches/` - kernel module sources and patches the scripts build or
|
||||||
|
apply, never inline in the shell code. Read them with `patch_file
|
||||||
|
<name>` (`lib/common.sh`, from the checkout); the bundle embeds every file
|
||||||
|
in `patches/` and overrides `patch_file`. Add new ones to its README.
|
||||||
- `lib/*.sh` - one file per responsibility, each defining functions only
|
- `lib/*.sh` - one file per responsibility, each defining functions only
|
||||||
(no top-level side effects besides constants). See the table in
|
(no top-level side effects besides constants). See the table in
|
||||||
`TECHNICAL.md`.
|
`TECHNICAL.md`.
|
||||||
@@ -25,6 +29,13 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
|
|||||||
`<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 live in `toggle_component`.
|
`gaming` must stay first. Dependencies live in `toggle_component`.
|
||||||
|
- **Feature versions.** Set `FEATURE_VERSION[<id>]` (`lib/menu.sh`) to the new
|
||||||
|
`VERSION` whenever what `<id>_enable` sets up changes (a new component gets
|
||||||
|
one too): installs recorded with an older version
|
||||||
|
are ticked and re-applied ("update" in the plan and the app). New default
|
||||||
|
sub-options are ticked for installs whose parent is on (`feature_new`).
|
||||||
|
Status still comes from the system; don't add ad-hoc `<id>_repair` checks
|
||||||
|
for new changes.
|
||||||
- **Reversibility.** Every per-user KDE setting a component changes goes
|
- **Reversibility.** Every per-user KDE setting a component changes goes
|
||||||
through `kset <component> <file> <group|group> <key> <value>`
|
through `kset <component> <file> <group|group> <key> <value>`
|
||||||
(`lib/state.sh`), which records the old value once; `<id>_disable` calls
|
(`lib/state.sh`), which records the old value once; `<id>_disable` calls
|
||||||
@@ -204,6 +215,22 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
|
|||||||
kernel only pairs that with a named lookup ("Port C") when the CEC driver
|
kernel only pairs that with a named lookup ("Port C") when the CEC driver
|
||||||
registered first, which never happens since amdgpu loads from the
|
registered first, which never happens since amdgpu loads from the
|
||||||
initramfs. Without it `/dev/cec0` exists but stays at `f.f.f.f`.
|
initramfs. Without it `/dev/cec0` exists but stays at `f.f.f.f`.
|
||||||
|
- Power-off fix (`poweroff`, `lib/fremont-poweroff.sh`, a default sub-option
|
||||||
|
of `machine`): recent kernels (7.2 and the 6.x/7.0/7.1 updates with the
|
||||||
|
backport) keep the firmware's S4/S5 wake bit on GPIO
|
||||||
|
pin 18, so the Steam Machine boots again right after powering off
|
||||||
|
(Valve's kernel clears it at probe, not for upstream). The DKMS module
|
||||||
|
`steamify-fremont-poweroff` (`patches/steamify-fremont-poweroff.c`) clears
|
||||||
|
it in a power-off-prepare handler, built for each kernel. It made the kernel pin
|
||||||
|
unnecessary: `kpin` is only available while on (`kpin_available`), and
|
||||||
|
`detect_components` unticks it, so a normal run removes an existing pin.
|
||||||
|
`hdmi` (it needed the pin) is retired the same way: `hdmi_status` is on
|
||||||
|
while anything of it is left (saved displays, hotplug unit, EDID files,
|
||||||
|
old kernel parameter), `hdmi_available` only then, it's always unticked,
|
||||||
|
and `hdmi_disable` removes all of it. CachyOS's
|
||||||
|
`linux-cachyos` is clang-built, `-bore` GCC-built: let DKMS pick the
|
||||||
|
compiler, never pass `LLVM=1`. Test shutdown on the real machine for every
|
||||||
|
new major kernel.
|
||||||
- `Relogin=true` means a gamescope that fails to start is relaunched in a
|
- `Relogin=true` means a gamescope that fails to start is relaunched in a
|
||||||
tight loop; keep that in mind when changing session handling.
|
tight loop; keep that in mind when changing session handling.
|
||||||
|
|
||||||
|
|||||||
@@ -5,6 +5,49 @@ 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.
|
||||||
|
|
||||||
|
## 2.2.0 - 2026-09-27
|
||||||
|
|
||||||
|
- **docs: Changelog entries per commit for 2.2.0**
|
||||||
|
- `32795bd` **feat(ui): Update badge on the progress screen**
|
||||||
|
- `3c8ca65` **fix(ui): Never write the sudo password to a file; Update label on the progress screen**
|
||||||
|
- The app gave `sudo` the password through a short-lived file; the helper
|
||||||
|
now reads it from a named pipe the app feeds from memory each time sudo
|
||||||
|
asks. The progress screen showed "undefined" for an update.
|
||||||
|
- `11a91ef` **feat: Update badge on components that get a newer version**
|
||||||
|
- The menu (terminal and app) shows "(update)" / an Update badge after
|
||||||
|
the name of a component a normal run will update.
|
||||||
|
- `163f790` **feat: Feature versions are Steamify versions; Steam Machine support is 2.2.0**
|
||||||
|
- Each component records the Steamify version of what it set up; when a
|
||||||
|
release changes a component, installs with an older version are ticked
|
||||||
|
and updated by a normal run ("update" in the plan and the app's
|
||||||
|
review). A setup from before counts as 2.1.0, so Steam Machine support
|
||||||
|
(2.2.0) is updated on machines set up with 2.1.0. New default
|
||||||
|
sub-options (like the power-off fix) are ticked where their parent is on.
|
||||||
|
- `e121b06` **fix: Remove whatever is left of HDMI refresh boost, pin or no pin**
|
||||||
|
- HDMI refresh boost needed the pinned kernel and is retired (newer
|
||||||
|
kernels do HDMI 2.1 themselves): whatever is left of it (saved
|
||||||
|
displays, also ones not connected, its hotplug script, the old kernel
|
||||||
|
parameter) is unticked and removed by the next run.
|
||||||
|
- `19ca966` **docs: Drop the kernel pin and HDMI refresh boost descriptions**
|
||||||
|
- `56fb908` **docs: Power-off fix texts cover every affected kernel, not just 7.2**
|
||||||
|
- `6df19eb` **feat: Power-off fix as its own sub-option, drop the kernel pin, feature versions**
|
||||||
|
- The power-off fix is a sub-option of Steam Machine support, ticked
|
||||||
|
along with it (opt-out). The kernel pin is no longer offered: an
|
||||||
|
existing pin is shown unticked, so the next run removes it and brings
|
||||||
|
back CachyOS's current kernel.
|
||||||
|
- `7694c97` **feat: Power-off fix for the Steam Machine; the kernel pin is optional**
|
||||||
|
- With recent kernels (newer 6.x, 7.0 and 7.1 updates, 7.2 and probably
|
||||||
|
later ones) the Steam Machine started again right after shutting down:
|
||||||
|
they keep the wake bit the firmware sets on GPIO pin 18, which Valve's
|
||||||
|
own kernel clears in a patch that won't reach CachyOS. A small module,
|
||||||
|
built with DKMS for each installed kernel, clears it right before
|
||||||
|
power-off. Tested with CachyOS 7.2.8: stays off, and without the module
|
||||||
|
it rebooted.
|
||||||
|
- Module sources and patches live in `patches/` instead of inside the
|
||||||
|
shell code; the single-file build and the app package include them. The
|
||||||
|
Steam Machine CEC driver change is now
|
||||||
|
`patches/cros-ec-cec-single-port.patch`.
|
||||||
|
|
||||||
## 2.1.0 - 2026-09-26
|
## 2.1.0 - 2026-09-26
|
||||||
|
|
||||||
- **feat(ui): Manage button for HDMI refresh boost**
|
- **feat(ui): Manage button for HDMI refresh boost**
|
||||||
|
|||||||
@@ -43,20 +43,11 @@ What Steamify can do on a Valve Steam Machine:
|
|||||||
TV's remote, and the TV turns on and off with the Steam Machine
|
TV's remote, and the TV turns on and off with the Steam Machine
|
||||||
([details](TECHNICAL.md#hdmi-cec)).
|
([details](TECHNICAL.md#hdmi-cec)).
|
||||||
- **Steam Machine support** - the front **LED bar** works, Steam's
|
- **Steam Machine support** - the front **LED bar** works, Steam's
|
||||||
**hardware settings** (fan, performance) work, and the power
|
**hardware settings** (fan, performance) work, and the power button puts
|
||||||
button puts it to sleep like a console.
|
it to sleep like a console.
|
||||||
- **Pin the kernel to 7.1.6-1** - on by default: with newer CachyOS kernels
|
- **Power-off fix** - on by default: shutting down really turns the Steam
|
||||||
the Steam Machine reboots instead of shutting down. Untick it once CachyOS
|
Machine off, also with the newest CachyOS kernel
|
||||||
fixes that. The kernel comes from its own
|
([details](TECHNICAL.md#power-off-fix-steam-machine)).
|
||||||
[release](https://github.com/theupriser/steamify-cachyos/releases/tag/kernel-7.1.6-1),
|
|
||||||
checked against CachyOS's signature
|
|
||||||
([details](TECHNICAL.md#kernel-pin-steam-machine)).
|
|
||||||
- **HDMI refresh boost** - only with the pinned kernel, never ticked by
|
|
||||||
default: gets the highest refresh rate your HDMI display runs at the
|
|
||||||
desktop resolution (e.g. 110 Hz instead of 60 on a 3440x1440 monitor).
|
|
||||||
Each step is shown and you confirm it; no answer within 15 s switches
|
|
||||||
back. For more (e.g. 175 Hz), use DisplayPort
|
|
||||||
([details](TECHNICAL.md#hdmi-refresh-boost-steam-machine)).
|
|
||||||
- **Update BIOS** - installs Valve's newest Steam Machine BIOS. Never ticked
|
- **Update BIOS** - installs Valve's newest Steam Machine BIOS. Never ticked
|
||||||
by default, at your own risk, and only after two warnings
|
by default, at your own risk, and only after two warnings
|
||||||
([details](TECHNICAL.md#bios-updates-steam-machine)).
|
([details](TECHNICAL.md#bios-updates-steam-machine)).
|
||||||
|
|||||||
+45
-74
@@ -208,84 +208,54 @@ upset CEC for the TV's other devices
|
|||||||
Turning it off removes the packages again (`linuxconsole` only if the
|
Turning it off removes the packages again (`linuxconsole` only if the
|
||||||
wizard installed it).
|
wizard installed it).
|
||||||
|
|
||||||
## Kernel pin (Steam Machine)
|
## Power-off fix (Steam Machine)
|
||||||
|
|
||||||
With CachyOS kernels newer than 7.1.6 a Steam Machine reboots instead of
|
With recent kernels a Steam Machine starts again right after powering off:
|
||||||
shutting down. The **Pin the kernel** sub-option (ticked along with Steam
|
the newer 6.x, 7.0 and 7.1 updates and 7.2 (and probably every kernel after
|
||||||
Machine support) installs `linux-cachyos` and `linux-cachyos-headers`
|
it). Linux stopped clearing the S4/S5 wake bits at probe
|
||||||
7.1.6-1 and adds them to `IgnorePkg` in `/etc/pacman.conf`, so updates skip
|
(`pinctrl-amd: Don't clear S4 wake bits at probe`, in 7.2 and backported to
|
||||||
them. DKMS builds the LED driver for it; restart to boot it.
|
stable kernels), and the firmware leaves
|
||||||
|
that bit set on GPIO pin 18 (`_SB.PCI0.GPP6`). Valve's own kernel
|
||||||
|
(`linux-neptune-72`) clears it at probe on Fremont, in a patch marked not
|
||||||
|
for upstream ("until the firmware is fixed"), so CachyOS and mainline won't
|
||||||
|
get it.
|
||||||
|
|
||||||
The packages (and their signatures, which pacman checks) are kept in
|
The **Power-off fix** sub-option of Steam Machine support (ticked along with
|
||||||
`/var/cache/steamify/kernel`, so re-applying needs no download. Missing
|
it, can be unticked) builds a small module with DKMS for every installed
|
||||||
files are taken from pacman's cache, else downloaded from this repo's
|
kernel, `steamify-fremont-poweroff` (source in
|
||||||
`kernel-7.1.6-1` release, then `archive.cachyos.org`, then
|
[`patches/steamify-fremont-poweroff.c`](patches/steamify-fremont-poweroff.c)).
|
||||||
`mirror.cachyos.org` (which only has the current kernel). Every file must
|
It only loads on Fremont (DMI board name) and touches one register: right
|
||||||
match the SHA-256 in the script and have a valid CachyOS signature; a bad
|
before power-off (a `SYS_OFF_MODE_POWER_OFF_PREPARE` handler, after the
|
||||||
one is deleted, so the next run downloads it again. Set `PINNED_KERNEL_URL`
|
drivers have shut down) it clears that pin's S4/S5 wake bit. On a kernel
|
||||||
to a directory URL with the files to try another source first, or drop them
|
that already clears it, it does nothing. It logs the pin's state at load:
|
||||||
into the kernel directory yourself.
|
`dmesg | grep 'GPIO 18'`; `/sys/kernel/debug/gpio` shows the S4/S5 column.
|
||||||
|
Tested on a Steam Machine with `linux-cachyos-bore` 7.2.8: it stayed off
|
||||||
|
three times in a row, and rebooted right away with the module unloaded.
|
||||||
|
|
||||||
Unticking it removes the pin and runs `sudo pacman -Syu`, which brings the
|
Where Steam Machine support was set up before 2.2.0, the menu ticks the
|
||||||
kernel back to CachyOS's current version; the files stay for next time.
|
power-off fix as a new default sub-option (see feature versions below), so a
|
||||||
|
normal run adds it.
|
||||||
|
|
||||||
## HDMI refresh boost (Steam Machine)
|
## Feature versions (updates)
|
||||||
|
|
||||||
With the pinned kernel (7.1.6), HDMI displays often stay at 60 Hz. Two
|
Each component has a feature version (`FEATURE_VERSION` in `lib/menu.sh`):
|
||||||
reasons:
|
the Steamify version in which what it sets up last changed (2.1.0 for
|
||||||
|
everything that hasn't changed since). After a component is turned on
|
||||||
|
successfully, that version is recorded in `~/.local/state/cachyos-gamescope-boot/features.state`
|
||||||
|
(turning it off records `off`). Whether a component is on is always checked
|
||||||
|
on the system itself; the version only decides about updates:
|
||||||
|
|
||||||
- Monitors list their fast modes in an extra EDID block, announced by the
|
- **On, recorded version older** than the current one: ticked and
|
||||||
HDMI Forum EEODB data block. 7.1.6 only reads the first extension block,
|
re-applied by a normal run; the plan (and the app's review) says
|
||||||
so it never sees them. Newer kernels do.
|
"update". A setup from before versions were recorded counts as 2.1.0,
|
||||||
- Their fastest modes need HDMI 2.1 (FRL). 7.1.6's amdgpu only does HDMI
|
so on a machine set up with 2.1.0, Steam Machine support (2.2.0) is
|
||||||
2.0 (TMDS, at most 600 MHz), but a mode with the display's own shortest
|
updated.
|
||||||
blanking at a slightly lower rate often fits.
|
- **A new default sub-option** (not in `NO_PRESELECT`) whose parent is on
|
||||||
|
and that was never turned on or off: ticked, so a normal run adds it.
|
||||||
|
Turning it off once records `off`, so it isn't ticked again.
|
||||||
|
|
||||||
The menu item (only on a Steam Machine with the pinned kernel, never
|
Set a component's entry to the new `VERSION` whenever what its
|
||||||
ticked by default, run from the desktop in Konsole):
|
`<id>_enable` sets up changes.
|
||||||
|
|
||||||
1. Takes the desktop resolution from KDE (`kscreen-doctor -j`) and reads the
|
|
||||||
display's complete EDID over DDC (`i2ctransfer`, segment pointer 0x30).
|
|
||||||
A live EDID left by an earlier test is cleared first.
|
|
||||||
2. Calculates the highest rate that fits: the display's TMDS limit (HDMI
|
|
||||||
Forum VSDB, capped at amdgpu's 600 MHz), its shortest blanking at that
|
|
||||||
resolution and its maximum refresh (range limits, VRR maximum). Steps:
|
|
||||||
that rate rounded down to ten, and the hundred below it as a safe option.
|
|
||||||
Rates the display already lists, or that aren't faster than what works
|
|
||||||
now, are left out.
|
|
||||||
3. Builds the EDID: all of the display's blocks, the block count and EEODB
|
|
||||||
fixed, plus a DisplayID block with the steps.
|
|
||||||
4. Loads it live (debugfs `edid_override`, `trigger_hotplug`) and switches
|
|
||||||
to each step, lowest first. Each one needs a "y" within 15 s
|
|
||||||
(`WIZARD_HDMI_CONFIRM_SECONDS` for tests); anything else switches back
|
|
||||||
and stops.
|
|
||||||
5. Saves the confirmed steps for that display: the EDID as
|
|
||||||
`/usr/lib/firmware/edid/steamify-<id>.bin`, where `<id>` is the display's
|
|
||||||
manufacturer, model, serial and date (EDID bytes 8-17), and a line in
|
|
||||||
`/etc/steamify/hdmi-edid.conf` (id, name, mode, rates). Other saved
|
|
||||||
displays are kept.
|
|
||||||
|
|
||||||
`steamify-edid.service` (at boot, before the login manager) and a udev rule
|
|
||||||
(`90-steamify-edid.rules`, every drm hotplug) run
|
|
||||||
`/usr/local/bin/steamify-edid-hotplug`. Per HDMI port it reads the connected
|
|
||||||
display's ID over DDC (the real display, even while an override is loaded)
|
|
||||||
and loads that display's saved EDID through debugfs, or resets the port to
|
|
||||||
the display's own EDID when there is none, or no display. What's loaded per
|
|
||||||
port is kept in `/run/steamify-edid`, so the hotplug the script triggers
|
|
||||||
itself doesn't loop.
|
|
||||||
|
|
||||||
The item is on when the connected display runs on its saved EDID; with
|
|
||||||
another display it's off, and ticking it sets that one up. Turning it off
|
|
||||||
removes the connected display's EDID; the unit and rule go with the last
|
|
||||||
one. In the app the item is a **Set up…** button, and **Manage** once a
|
|
||||||
display is saved: it lists every saved display, removes any of them, and
|
|
||||||
sets up the connected display when it has none. Unpinning the kernel
|
|
||||||
removes them all.
|
|
||||||
|
|
||||||
Versions before 2.1.0 used `drm.edid_firmware=` on the kernel command line
|
|
||||||
(and the initramfs), which applied to any display on that port; re-applying
|
|
||||||
saves such a setup per display and removes the parameter. Untick it before removing the
|
|
||||||
kernel pin: newer kernels read the EDID themselves and can do HDMI 2.1.
|
|
||||||
|
|
||||||
## BIOS updates (Steam Machine)
|
## BIOS updates (Steam Machine)
|
||||||
|
|
||||||
@@ -355,9 +325,10 @@ immediately, which can turn into a loop - see
|
|||||||
| `lib/wizard-shortcut.sh` | Steamify shortcut: desktop icon and launcher entry that run the newest release |
|
| `lib/wizard-shortcut.sh` | Steamify shortcut: desktop icon and launcher entry that run the newest release |
|
||||||
| `lib/bios.sh` | Update BIOS (Steam Machine, opt-in): current/newest version, double confirmation, fwupd |
|
| `lib/bios.sh` | Update BIOS (Steam Machine, opt-in): current/newest version, double confirmation, fwupd |
|
||||||
| `lib/cec.sh` | HDMI-CEC: Valve's `cecd` and friends from its `holo` repository |
|
| `lib/cec.sh` | HDMI-CEC: Valve's `cecd` and friends from its `holo` repository |
|
||||||
| `lib/steam-machine.sh` | Steam Machine support: LED driver, LED access, steamos-manager; kernel pin |
|
| `lib/steam-machine.sh` | Steam Machine support: LED driver, LED access, steamos-manager |
|
||||||
| `lib/hdmi-refresh.sh` | HDMI refresh boost (Steam Machine, pinned kernel): EDID over DDC, calculated steps, live test, `drm.edid_firmware` |
|
| `lib/fremont-poweroff.sh` | Steam Machine support: the power-off fix (DKMS module from `patches/`) |
|
||||||
| `.github/tools/bundle.sh` | Builds the single-file version (`dist/steamify.sh`) |
|
| `patches/` | Module sources and patches the scripts build or apply (`patch_file`); see its README |
|
||||||
|
| `.github/tools/bundle.sh` | Builds the single-file version (`dist/steamify.sh`), with `patches/` embedded |
|
||||||
| `.github/workflows/bundle.yml` | Builds and checks it on every push; publishes it on `main` |
|
| `.github/workflows/bundle.yml` | Builds and checks it on every push; publishes it on `main` |
|
||||||
|
|
||||||
The single-file version is generated: on every push to `main`, GitHub
|
The single-file version is generated: on every push to `main`, GitHub
|
||||||
|
|||||||
+9
-2
@@ -62,6 +62,7 @@ backend_status() {
|
|||||||
items+=",\"kind\":\"$kind\",\"parent\":$(json_str "$parent")"
|
items+=",\"kind\":\"$kind\",\"parent\":$(json_str "$parent")"
|
||||||
items+=",\"on\":$( [[ "$now" == 1 ]] && echo true || echo false)"
|
items+=",\"on\":$( [[ "$now" == 1 ]] && echo true || echo false)"
|
||||||
items+=",\"wanted\":$( [[ "${WANTED[$c]:-0}" == 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+=",\"selectable\":$(component_selectable "$c" && echo true || echo false)}"
|
items+=",\"selectable\":$(component_selectable "$c" && echo true || echo false)}"
|
||||||
done
|
done
|
||||||
local cec="" f
|
local cec="" f
|
||||||
@@ -160,6 +161,8 @@ backend_apply() {
|
|||||||
# The same rules as the menu's toggles.
|
# The same rules as the menu's toggles.
|
||||||
[[ "${WANTED[single]:-0}" == 1 ]] && WANTED[gaming]=1
|
[[ "${WANTED[single]:-0}" == 1 ]] && WANTED[gaming]=1
|
||||||
[[ "${WANTED[gaming]:-0}" == 0 ]] && { WANTED[single]=0; WANTED[boot]=0; }
|
[[ "${WANTED[gaming]:-0}" == 0 ]] && { WANTED[single]=0; WANTED[boot]=0; }
|
||||||
|
[[ "${WANTED[poweroff]:-0}" == 1 ]] && WANTED[machine]=1
|
||||||
|
[[ "${WANTED[machine]:-0}" == 0 ]] && WANTED[poweroff]=0
|
||||||
[[ "${WANTED[kpin]:-0}" == 1 ]] && WANTED[machine]=1
|
[[ "${WANTED[kpin]:-0}" == 1 ]] && WANTED[machine]=1
|
||||||
[[ "${WANTED[machine]:-0}" == 0 ]] && WANTED[kpin]=0
|
[[ "${WANTED[machine]:-0}" == 0 ]] && WANTED[kpin]=0
|
||||||
[[ "${WANTED[hdmi]:-0}" == 1 ]] && { WANTED[machine]=1; WANTED[kpin]=1; }
|
[[ "${WANTED[hdmi]:-0}" == 1 ]] && { WANTED[machine]=1; WANTED[kpin]=1; }
|
||||||
@@ -179,8 +182,12 @@ backend_apply() {
|
|||||||
LOGIN_MANAGER=plasmalogin
|
LOGIN_MANAGER=plasmalogin
|
||||||
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
|
[[ "${WANTED[single]}" == 1 ]] && LOGIN_MANAGER=sddm
|
||||||
local -a failed=()
|
local -a failed=()
|
||||||
for c in "${TO_DISABLE[@]}"; do backend_run_component "$c" disable || failed+=("$c"); done
|
for c in "${TO_DISABLE[@]}"; do
|
||||||
for c in "${TO_ENABLE[@]}"; do backend_run_component "$c" enable || failed+=("$c"); done
|
if backend_run_component "$c" disable; then feature_record "$c" disable; else failed+=("$c"); fi
|
||||||
|
done
|
||||||
|
for c in "${TO_ENABLE[@]}"; do
|
||||||
|
if backend_run_component "$c" enable; then is_action "$c" || feature_record "$c" enable; else failed+=("$c"); fi
|
||||||
|
done
|
||||||
[[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " =~ \ (gaming|single|boot|kpin)\ ]] && RESTART_FOR_LOGIN=true
|
[[ " ${TO_DISABLE[*]} ${TO_ENABLE[*]} " =~ \ (gaming|single|boot|kpin)\ ]] && RESTART_FOR_LOGIN=true
|
||||||
backend_event finished "\"failed\":$(json_list "${failed[@]}"),\"restart\":$(restart_needed && echo true || echo false)"
|
backend_event finished "\"failed\":$(json_list "${failed[@]}"),\"restart\":$(restart_needed && echo true || echo false)"
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-9
@@ -60,8 +60,8 @@ cec_reload_driver() {
|
|||||||
cec_driver_enable() {
|
cec_driver_enable() {
|
||||||
detect_valve_fremont || return 0
|
detect_valve_fremont || return 0
|
||||||
local tmp k
|
local tmp k
|
||||||
if ! pacman -Q dkms >/dev/null 2>&1; then
|
if ! pacman -Q dkms patch >/dev/null 2>&1; then
|
||||||
sudo pacman -S --needed --noconfirm dkms || { 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
|
||||||
tmp="$(mktemp -d)"
|
tmp="$(mktemp -d)"
|
||||||
@@ -72,13 +72,8 @@ cec_driver_enable() {
|
|||||||
err "Downloading Valve's CEC driver failed (or its checksum didn't match)."
|
err "Downloading Valve's CEC driver failed (or its checksum didn't match)."
|
||||||
return 1
|
return 1
|
||||||
fi
|
fi
|
||||||
# amdgpu registers its HDMI notifier without a port name, which only
|
# So it finds amdgpu's HDMI port (see the patch for why).
|
||||||
# matches the driver's named lookup ("Port C") if the driver loaded
|
patch_file cros-ec-cec-single-port.patch | patch -s -d "$tmp" -p1 ||
|
||||||
# first; amdgpu loads from the initramfs, so it never does. With a
|
|
||||||
# single CEC port, look it up by device alone.
|
|
||||||
sed -i 's/cec_notifier_cec_adap_register(hdmi_dev, conns\[port_num\],/cec_notifier_cec_adap_register(hdmi_dev, conns[1] ? conns[port_num] : NULL,/' \
|
|
||||||
"$tmp/cros-ec-cec.c"
|
|
||||||
grep -q 'conns\[1\] ? conns\[port_num\] : NULL' "$tmp/cros-ec-cec.c" ||
|
|
||||||
{ rm -rf "$tmp"; err "Patching Valve's CEC driver failed."; return 1; }
|
{ rm -rf "$tmp"; err "Patching Valve's CEC driver failed."; return 1; }
|
||||||
echo 'obj-m += cros-ec-cec.o' >"$tmp/Makefile"
|
echo 'obj-m += cros-ec-cec.o' >"$tmp/Makefile"
|
||||||
printf '%s\n' "# Written by Steamify: Valve's cros_ec_cec, which knows the Steam Machine." \
|
printf '%s\n' "# Written by Steamify: Valve's cros_ec_cec, which knows the Steam Machine." \
|
||||||
|
|||||||
@@ -6,6 +6,9 @@ c_reset="\033[0m"; c_bold="\033[1m"; c_green="\033[32m"; c_yellow="\033[33m"; c_
|
|||||||
|
|
||||||
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} $*"; }
|
||||||
|
# patch_file <name>: a file from patches/ (module sources, patches). The
|
||||||
|
# single-file build replaces this with one that has them embedded.
|
||||||
|
patch_file() { cat "$SCRIPT_DIR/patches/$1"; }
|
||||||
warn() { echo -e "${c_yellow}[WARN]${c_reset} $*"; }
|
warn() { echo -e "${c_yellow}[WARN]${c_reset} $*"; }
|
||||||
err() { echo -e "${c_red}[ERROR] $*${c_reset}" >&2; }
|
err() { echo -e "${c_red}[ERROR] $*${c_reset}" >&2; }
|
||||||
ask_yn() {
|
ask_yn() {
|
||||||
|
|||||||
@@ -0,0 +1,70 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
# "Power-off fix" menu item (poweroff), a sub-option of Steam Machine support
|
||||||
|
# ticked along with it (opt-out): the Steam Machine powers off instead of
|
||||||
|
# booting up again. Recent kernels (7.2, and the 6.x, 7.0 and 7.1 updates
|
||||||
|
# that got the change backported) keep the S4/S5 wake bit the firmware
|
||||||
|
# leaves set on GPIO pin 18 ("pinctrl-amd: Don't clear S4 wake bits at
|
||||||
|
# probe"), so the machine starts again right after powering off. Valve's kernel clears it at probe on Fremont, in
|
||||||
|
# a patch not meant for upstream, so CachyOS won't get it. A small module
|
||||||
|
# (patches/steamify-fremont-poweroff.c) clears it right before power-off;
|
||||||
|
# DKMS builds it for each installed kernel. On a kernel that clears it
|
||||||
|
# itself it does nothing.
|
||||||
|
# Sourced by steamify.sh; not meant to be run on its own.
|
||||||
|
|
||||||
|
POWEROFF_DKMS_NAME=steamify-fremont-poweroff
|
||||||
|
POWEROFF_DKMS_VER=1
|
||||||
|
POWEROFF_DKMS_SRC="/usr/src/$POWEROFF_DKMS_NAME-$POWEROFF_DKMS_VER"
|
||||||
|
POWEROFF_MODULES_LOAD=/etc/modules-load.d/steamify-fremont-poweroff.conf
|
||||||
|
|
||||||
|
poweroff_fix_installed() { [[ -f "$POWEROFF_DKMS_SRC/dkms.conf" && -f "$POWEROFF_MODULES_LOAD" ]]; }
|
||||||
|
|
||||||
|
poweroff_fix_enable() {
|
||||||
|
detect_valve_fremont || return 0
|
||||||
|
local tmp k
|
||||||
|
if ! pacman -Q dkms >/dev/null 2>&1; then
|
||||||
|
sudo pacman -S --needed --noconfirm dkms || { err "Installing dkms failed."; return 1; }
|
||||||
|
fi
|
||||||
|
install_kernel_headers || return 1
|
||||||
|
tmp="$(mktemp -d)"
|
||||||
|
patch_file "$POWEROFF_DKMS_NAME.c" > "$tmp/$POWEROFF_DKMS_NAME.c"
|
||||||
|
echo "obj-m += $POWEROFF_DKMS_NAME.o" >"$tmp/Makefile"
|
||||||
|
printf '%s\n' "# Written by Steamify: the Steam Machine powers off instead of booting up again." \
|
||||||
|
"PACKAGE_NAME=\"$POWEROFF_DKMS_NAME\"" "PACKAGE_VERSION=\"$POWEROFF_DKMS_VER\"" \
|
||||||
|
"BUILT_MODULE_NAME[0]=\"$POWEROFF_DKMS_NAME\"" 'DEST_MODULE_LOCATION[0]="/updates/dkms"' \
|
||||||
|
'AUTOINSTALL="yes"' >"$tmp/dkms.conf"
|
||||||
|
sudo dkms remove "$POWEROFF_DKMS_NAME/$POWEROFF_DKMS_VER" --all >/dev/null 2>&1
|
||||||
|
sudo rm -rf "$POWEROFF_DKMS_SRC"
|
||||||
|
sudo install -d "$POWEROFF_DKMS_SRC" &&
|
||||||
|
sudo install -m644 "$tmp"/{"$POWEROFF_DKMS_NAME.c",Makefile,dkms.conf} "$POWEROFF_DKMS_SRC/"
|
||||||
|
rm -rf "$tmp"
|
||||||
|
sudo dkms add "$POWEROFF_DKMS_NAME/$POWEROFF_DKMS_VER" >/dev/null ||
|
||||||
|
{ err "Adding the power-off fix to DKMS failed."; return 1; }
|
||||||
|
for k in /usr/lib/modules/*/build; do
|
||||||
|
k="$(basename "$(dirname "$k")")"
|
||||||
|
info "Building the power-off fix for $k..."
|
||||||
|
sudo dkms install "$POWEROFF_DKMS_NAME/$POWEROFF_DKMS_VER" -k "$k" >/dev/null ||
|
||||||
|
warn "Building the power-off fix for $k failed; with that kernel it may start again after shutting down."
|
||||||
|
done
|
||||||
|
echo "$POWEROFF_DKMS_NAME" | sudo tee "$POWEROFF_MODULES_LOAD" >/dev/null
|
||||||
|
sudo modprobe "$POWEROFF_DKMS_NAME" 2>/dev/null
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
poweroff_fix_disable() {
|
||||||
|
sudo rm -f "$POWEROFF_MODULES_LOAD"
|
||||||
|
sudo modprobe -r "$POWEROFF_DKMS_NAME" 2>/dev/null
|
||||||
|
[[ -d "$POWEROFF_DKMS_SRC" ]] || return 0
|
||||||
|
sudo dkms remove "$POWEROFF_DKMS_NAME/$POWEROFF_DKMS_VER" --all >/dev/null 2>&1
|
||||||
|
sudo rm -rf "$POWEROFF_DKMS_SRC"
|
||||||
|
}
|
||||||
|
|
||||||
|
poweroff_status() { poweroff_fix_installed; }
|
||||||
|
poweroff_enable() {
|
||||||
|
info "Installing the power-off fix (the machine stays off after shutting down)..."
|
||||||
|
poweroff_fix_enable || { err "Installing the power-off fix failed."; return 1; }
|
||||||
|
ok "Power-off fix on: the Steam Machine stays off after shutting down."
|
||||||
|
}
|
||||||
|
poweroff_disable() {
|
||||||
|
poweroff_fix_disable
|
||||||
|
ok "Power-off fix removed; with recent kernels the Steam Machine may start again after shutting down."
|
||||||
|
}
|
||||||
+13
-20
@@ -56,18 +56,17 @@ hdmi_cmdline_param() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hdmi_available() {
|
hdmi_available() {
|
||||||
# Stays available while on, so it can be turned off after the pin is gone.
|
# No longer offered (it needed the pinned kernel): only shown while
|
||||||
detect_valve_fremont || return 1
|
# anything of it is left, so a normal run removes it.
|
||||||
# Saved displays too, so they can be removed without the pin.
|
detect_valve_fremont && hdmi_status
|
||||||
hdmi_status || [[ -n "$(hdmi_saved)" ]] || { pinned_kernel_installed && [[ -n "$(hdmi_connectors)" ]]; }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
hdmi_status() {
|
hdmi_status() {
|
||||||
# On = the connected display runs on its saved EDID. Another display on
|
# On while anything of it is left: saved displays (connected or not), the
|
||||||
# the port is off, so ticking it sets that one up. The command line: set
|
# hotplug script, an EDID file, or the kernel parameter of versions
|
||||||
# up by an older version, re-applying moves it over.
|
# before 2.1.0.
|
||||||
[[ -n "$(hdmi_active_ids)" ]] ||
|
[[ -n "$(hdmi_saved)" || -f "$HDMI_UNIT" || -n "$(hdmi_cmdline_param)" ]] ||
|
||||||
{ [[ -n "$(hdmi_cmdline_param)" ]] && compgen -G "$HDMI_FW_DIR/steamify-*.bin" >/dev/null; }
|
compgen -G "$HDMI_FW_DIR/steamify-*.bin" >/dev/null
|
||||||
}
|
}
|
||||||
|
|
||||||
hdmi_saved() { [[ -f "$HDMI_MAP" ]] && grep -v '^#' "$HDMI_MAP"; }
|
hdmi_saved() { [[ -f "$HDMI_MAP" ]] && grep -v '^#' "$HDMI_MAP"; }
|
||||||
@@ -688,16 +687,10 @@ hdmi_enable() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
hdmi_disable() {
|
hdmi_disable() {
|
||||||
# For the connected display; other saved displays stay (the app lists
|
# Everything, for every saved display: newer kernels read the whole EDID
|
||||||
# them). Without the pinned kernel none may stay: newer kernels read the
|
# themselves, and an old one loaded at the next hotplug would be wrong.
|
||||||
# EDID themselves.
|
|
||||||
local id
|
|
||||||
if [[ -n "$(hdmi_cmdline_param)" ]]; then
|
|
||||||
hdmi_forget all; hdmi_remove_boot_param || return 1; state_clear hdmi
|
|
||||||
elif [[ "${WANTED[kpin]:-1}" == 0 ]] || ! pinned_kernel_installed; then
|
|
||||||
hdmi_forget all
|
hdmi_forget all
|
||||||
else
|
hdmi_remove_boot_param || return 1
|
||||||
for id in $(hdmi_active_ids); do hdmi_forget "$id"; done
|
state_clear hdmi
|
||||||
fi
|
ok "HDMI refresh boost removed; displays use their own EDID again."
|
||||||
ok "HDMI refresh boost removed for the connected display; it uses its own EDID again."
|
|
||||||
}
|
}
|
||||||
+75
-12
@@ -5,18 +5,30 @@
|
|||||||
|
|
||||||
# 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 theme glyphs single launcher cec machine kpin hdmi bios)
|
COMPONENTS=(gaming boot theme glyphs single launcher 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 [kpin]=machine [hdmi]=machine [bios]=machine)
|
declare -A PARENT=([boot]=gaming [poweroff]=machine [kpin]=machine [hdmi]=machine [bios]=machine)
|
||||||
# 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.
|
# screen to confirm each step.
|
||||||
NO_PRESELECT=(boot cec hdmi)
|
NO_PRESELECT=(boot cec kpin hdmi)
|
||||||
|
# Feature versions: the Steamify version in which what a component's enable
|
||||||
|
# sets up last changed; set it to the new VERSION whenever you change one.
|
||||||
|
# Each successful run records it (state "features"); a component that's on
|
||||||
|
# with an older one is ticked and re-applied by a normal run, and the app
|
||||||
|
# shows it as an update. A setup from before these were recorded counts as
|
||||||
|
# FEATURE_BASELINE.
|
||||||
|
FEATURE_BASELINE=2.1.0
|
||||||
|
declare -A FEATURE_VERSION=(
|
||||||
|
[gaming]=2.1.0 [boot]=2.1.0 [theme]=2.1.0 [glyphs]=2.1.0 [single]=2.1.0
|
||||||
|
[launcher]=2.1.0 [cec]=2.1.0 [machine]=2.2.0 [poweroff]=2.2.0
|
||||||
|
[kpin]=2.1.0 [hdmi]=2.1.0
|
||||||
|
)
|
||||||
|
|
||||||
declare -A LABEL=(
|
declare -A LABEL=(
|
||||||
[gaming]="SteamOS conversion: boot into gaming mode, Steam on the desktop"
|
[gaming]="SteamOS conversion: boot into gaming mode, Steam on the desktop"
|
||||||
@@ -27,7 +39,8 @@ declare -A LABEL=(
|
|||||||
[launcher]="Steamify shortcut: the app on the desktop, Steamify Terminal in the launcher"
|
[launcher]="Steamify shortcut: the app on the desktop, Steamify Terminal in the launcher"
|
||||||
[cec]="HDMI-CEC: use Steam with the TV remote, TV on/off with the PC (experimental)"
|
[cec]="HDMI-CEC: use Steam with the TV remote, TV on/off with the PC (experimental)"
|
||||||
[machine]="Steam Machine support: LED bar driver, hardware settings in Steam"
|
[machine]="Steam Machine support: LED bar driver, hardware settings in Steam"
|
||||||
[kpin]="Pin the kernel to $PINNED_KERNEL_VER (fixes rebooting after shutdown)"
|
[poweroff]="Power-off fix: the Steam Machine stays off after shutting down"
|
||||||
|
[kpin]="Pin the kernel to $PINNED_KERNEL_VER (untick for CachyOS's current kernel)"
|
||||||
[hdmi]="HDMI refresh boost: highest refresh your HDMI display runs"
|
[hdmi]="HDMI refresh boost: highest refresh your HDMI display runs"
|
||||||
[bios]="Update BIOS"
|
[bios]="Update BIOS"
|
||||||
)
|
)
|
||||||
@@ -35,7 +48,8 @@ declare -A CURRENT WANTED
|
|||||||
|
|
||||||
component_available() {
|
component_available() {
|
||||||
case "$1" in
|
case "$1" in
|
||||||
machine|kpin) machine_available ;;
|
machine|poweroff) machine_available ;;
|
||||||
|
kpin) kpin_available ;;
|
||||||
hdmi) hdmi_available ;;
|
hdmi) hdmi_available ;;
|
||||||
bios) bios_available ;;
|
bios) bios_available ;;
|
||||||
esac
|
esac
|
||||||
@@ -56,6 +70,42 @@ menu_visible() {
|
|||||||
[[ -z "${PARENT[$1]:-}" || "${WANTED[${PARENT[$1]}]:-0}" == 1 ]]
|
[[ -z "${PARENT[$1]:-}" || "${WANTED[${PARENT[$1]}]:-0}" == 1 ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
feature_record() {
|
||||||
|
# feature_record <component> <enable|disable>: after a successful run.
|
||||||
|
if [[ "$2" == enable ]]; then state_set features "$1" "${FEATURE_VERSION[$1]:-$FEATURE_BASELINE}"
|
||||||
|
else state_set features "$1" off; fi
|
||||||
|
}
|
||||||
|
|
||||||
|
feature_outdated() {
|
||||||
|
# On, but set up by an older version of that feature (compared as
|
||||||
|
# versions: 2.10.0 is newer than 2.9.0).
|
||||||
|
local have want
|
||||||
|
[[ "${CURRENT[$1]:-0}" == 1 ]] || return 1
|
||||||
|
have="$(state_get features "$1" "$FEATURE_BASELINE")"
|
||||||
|
[[ "$have" == off ]] && have="$FEATURE_BASELINE"
|
||||||
|
want="${FEATURE_VERSION[$1]:-$FEATURE_BASELINE}"
|
||||||
|
[[ "$have" != "$want" && "$(printf '%s\n' "$have" "$want" | sort -V | head -n 1)" == "$have" ]]
|
||||||
|
}
|
||||||
|
|
||||||
|
menu_label() {
|
||||||
|
# The label, with "(update)" after the name when a newer version of it
|
||||||
|
# will be applied.
|
||||||
|
local l="${LABEL[$1]}"
|
||||||
|
if feature_outdated "$1"; then
|
||||||
|
if [[ "$l" == *:* ]]; then l="${l%%:*} ${c_yellow}(update)${c_reset}:${l#*:}"
|
||||||
|
else l+=" ${c_yellow}(update)${c_reset}"; fi
|
||||||
|
fi
|
||||||
|
printf '%s' "$l"
|
||||||
|
}
|
||||||
|
|
||||||
|
feature_new() {
|
||||||
|
# A default sub-option added after its parent was set up (e.g. the
|
||||||
|
# power-off fix under Steam Machine support): never turned on or off.
|
||||||
|
[[ -n "${PARENT[$1]:-}" && "${CURRENT[${PARENT[$1]}]:-0}" == 1 && "${CURRENT[$1]:-0}" == 0 ]] &&
|
||||||
|
! is_action "$1" && [[ " ${NO_PRESELECT[*]} " != *" $1 "* ]] &&
|
||||||
|
[[ -z "$(state_get features "$1")" ]]
|
||||||
|
}
|
||||||
|
|
||||||
component_selectable() {
|
component_selectable() {
|
||||||
# Greyed out and not tickable when it has nothing to do.
|
# Greyed out and not tickable when it has nothing to do.
|
||||||
case "$1" in
|
case "$1" in
|
||||||
@@ -72,6 +122,17 @@ detect_components() {
|
|||||||
done
|
done
|
||||||
# HDMI-CEC set up by an older version: tick it, so a normal run fixes it.
|
# HDMI-CEC set up by an older version: tick it, so a normal run fixes it.
|
||||||
component_available cec && cec_repair && WANTED[cec]=1
|
component_available cec && cec_repair && WANTED[cec]=1
|
||||||
|
# Updated features and new default sub-options: ticked, so a normal run
|
||||||
|
# applies them.
|
||||||
|
for c in "${COMPONENTS[@]}"; do
|
||||||
|
component_available "$c" || continue
|
||||||
|
{ feature_outdated "$c" || feature_new "$c"; } && WANTED[$c]=1
|
||||||
|
done
|
||||||
|
# The kernel pin is no longer needed (the power-off fix), and HDMI
|
||||||
|
# refresh boost needed the pin: whatever is left of either is unticked,
|
||||||
|
# so a normal run removes it.
|
||||||
|
component_available kpin && WANTED[kpin]=0
|
||||||
|
component_available hdmi && WANTED[hdmi]=0
|
||||||
# The terminal-only Steamify shortcut from before 2.0.1: tick it, so a
|
# The terminal-only Steamify shortcut from before 2.0.1: tick it, so a
|
||||||
# normal run replaces it with the app.
|
# normal run replaces it with the app.
|
||||||
launcher_repair && WANTED[launcher]=1
|
launcher_repair && WANTED[launcher]=1
|
||||||
@@ -96,8 +157,10 @@ toggle_component() {
|
|||||||
if [[ "$c" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[single]=0; WANTED[boot]=0; fi
|
if [[ "$c" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[single]=0; WANTED[boot]=0; fi
|
||||||
# Where to boot to is part of the conversion, too.
|
# Where to boot to is part of the conversion, too.
|
||||||
if [[ "$c" == boot && "${WANTED[boot]}" == 1 ]]; then WANTED[gaming]=1; fi
|
if [[ "$c" == boot && "${WANTED[boot]}" == 1 ]]; then WANTED[gaming]=1; fi
|
||||||
# The kernel pin is opt-out: ticked along with Steam Machine support.
|
# The power-off fix is opt-out: ticked along with Steam Machine support.
|
||||||
if [[ "$c" == machine ]]; then WANTED[kpin]=${WANTED[machine]}; fi
|
if [[ "$c" == machine ]]; then WANTED[poweroff]=${WANTED[machine]}; fi
|
||||||
|
if [[ "$c" == poweroff && "${WANTED[poweroff]}" == 1 ]]; then WANTED[machine]=1; fi
|
||||||
|
if [[ "$c" == machine && "${WANTED[machine]}" == 0 ]]; then WANTED[kpin]=0; fi
|
||||||
if [[ "$c" == kpin && "${WANTED[kpin]}" == 1 ]]; then WANTED[machine]=1; fi
|
if [[ "$c" == kpin && "${WANTED[kpin]}" == 1 ]]; then WANTED[machine]=1; fi
|
||||||
# HDMI refresh boost and the BIOS update sit under Steam Machine support;
|
# HDMI refresh boost and the BIOS update sit under Steam Machine support;
|
||||||
# the boost also needs the pinned kernel (newer kernels don't need it).
|
# the boost also needs the pinned kernel (newer kernels don't need it).
|
||||||
@@ -131,9 +194,9 @@ show_menu() {
|
|||||||
local tree=""; [[ -n "${PARENT[$c]:-}" ]] && tree=" └ "
|
local tree=""; [[ -n "${PARENT[$c]:-}" ]] && tree=" └ "
|
||||||
printf " %b%-3s %-6s %-6s %s%s (not available)%b\n" "$c_dim" "$i" "$now" "$want" "$tree" "${LABEL[$c]}" "$c_reset"
|
printf " %b%-3s %-6s %-6s %s%s (not available)%b\n" "$c_dim" "$i" "$now" "$want" "$tree" "${LABEL[$c]}" "$c_reset"
|
||||||
elif [[ -n "${PARENT[$c]:-}" ]]; then
|
elif [[ -n "${PARENT[$c]:-}" ]]; then
|
||||||
printf " %-3s %b %-6s └ %s\n" "$i" "$now" "$want" "${LABEL[$c]}"
|
printf " %-3s %b %-6s └ %b\n" "$i" "$now" "$want" "$(menu_label "$c")"
|
||||||
else
|
else
|
||||||
printf " %-3s %b %-6s %s\n" "$i" "$now" "$want" "${LABEL[$c]}"
|
printf " %-3s %b %-6s %b\n" "$i" "$now" "$want" "$(menu_label "$c")"
|
||||||
fi
|
fi
|
||||||
done
|
done
|
||||||
echo
|
echo
|
||||||
@@ -266,7 +329,7 @@ plan_changes() {
|
|||||||
component_available "$c" || continue
|
component_available "$c" || continue
|
||||||
[[ "${WANTED[$c]}" == 1 ]] || 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 ]] ||
|
if [[ "${CURRENT[$c]}" == 0 || "$REAPPLY" == true ]] || feature_outdated "$c" ||
|
||||||
[[ "$c" == gaming && "${CURRENT[single]}" != "${WANTED[single]}" ]]; then
|
[[ "$c" == gaming && "${CURRENT[single]}" != "${WANTED[single]}" ]]; then
|
||||||
TO_ENABLE+=("$c")
|
TO_ENABLE+=("$c")
|
||||||
fi
|
fi
|
||||||
@@ -281,13 +344,13 @@ apply_changes() {
|
|||||||
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}"
|
||||||
else echo; echo -e "${c_bold}Turning off: ${LABEL[$c]}${c_reset}"; fi
|
else echo; echo -e "${c_bold}Turning off: ${LABEL[$c]}${c_reset}"; fi
|
||||||
"${c}_disable" || failed+=("$c")
|
if "${c}_disable"; then feature_record "$c" disable; else failed+=("$c"); fi
|
||||||
done
|
done
|
||||||
for c in "${TO_ENABLE[@]}"; do
|
for c in "${TO_ENABLE[@]}"; do
|
||||||
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: desktop${c_reset}"
|
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: desktop${c_reset}"
|
||||||
elif is_action "$c"; then echo; echo -e "${c_bold}Running: ${LABEL[$c]%%:*}${c_reset}"
|
elif is_action "$c"; then echo; echo -e "${c_bold}Running: ${LABEL[$c]%%:*}${c_reset}"
|
||||||
else echo; echo -e "${c_bold}Turning on: ${LABEL[$c]}${c_reset}"; fi
|
else echo; echo -e "${c_bold}Turning on: ${LABEL[$c]}${c_reset}"; fi
|
||||||
"${c}_enable" || failed+=("$c")
|
if "${c}_enable"; then is_action "$c" || feature_record "$c" enable; else failed+=("$c"); fi
|
||||||
done
|
done
|
||||||
FAILED=("${failed[@]}")
|
FAILED=("${failed[@]}")
|
||||||
}
|
}
|
||||||
+12
-8
@@ -1,6 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
# "Steam Machine support" menu item, only on Valve Fremont hardware: the
|
# "Steam Machine support" menu item, only on Valve Fremont hardware: the
|
||||||
# front LED bar driver, LED access for Steam, and steamos-manager.
|
# front LED bar driver, LED access for Steam and steamos-manager. Its
|
||||||
|
# sub-option "Power-off fix" is lib/fremont-poweroff.sh.
|
||||||
# Sourced by steamify.sh; not meant to be run on its own.
|
# Sourced by steamify.sh; not meant to be run on its own.
|
||||||
|
|
||||||
detect_valve_fremont() {
|
detect_valve_fremont() {
|
||||||
@@ -14,8 +15,8 @@ detect_valve_fremont() {
|
|||||||
[[ "$vendor" == "OEM" && "$product" == "F7F" ]]
|
[[ "$vendor" == "OEM" && "$product" == "F7F" ]]
|
||||||
}
|
}
|
||||||
|
|
||||||
# Kernel pinned on a Steam Machine: with newer linux-cachyos releases it
|
# Kernel pinned on a Steam Machine (optional): newer linux-cachyos releases
|
||||||
# reboots instead of shutting down. The packages are kept in
|
# 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
|
# PINNED_KERNEL_DIR, so re-applying (or reinstalling after an update slipped
|
||||||
# through) needs no download.
|
# through) needs no download.
|
||||||
PINNED_KERNEL_VER="7.1.6-1"
|
PINNED_KERNEL_VER="7.1.6-1"
|
||||||
@@ -148,7 +149,9 @@ remove_kernel_pin() {
|
|||||||
RESTART_FOR_LOGIN=true
|
RESTART_FOR_LOGIN=true
|
||||||
}
|
}
|
||||||
|
|
||||||
# "Pin the kernel" menu item, a sub-option of Steam Machine support.
|
# "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() {
|
kpin_status() {
|
||||||
pinned_kernel_installed && grep -Eq '^IgnorePkg\s*=.*\slinux-cachyos(\s|$)' /etc/pacman.conf
|
pinned_kernel_installed && grep -Eq '^IgnorePkg\s*=.*\slinux-cachyos(\s|$)' /etc/pacman.conf
|
||||||
}
|
}
|
||||||
@@ -334,13 +337,13 @@ reload_powerdevil() {
|
|||||||
kernel_overview() {
|
kernel_overview() {
|
||||||
# Per installed kernel: headers (needed to build DKMS modules), the
|
# Per installed kernel: headers (needed to build DKMS modules), the
|
||||||
# in-kernel Steam controller driver, and, on a Steam Machine, whether the
|
# in-kernel Steam controller driver, and, on a Steam Machine, whether the
|
||||||
# LED driver is built for it. Printed in the menu so it's easy to verify
|
# LED driver and the power-off fix are built for it. Printed in the menu so it's easy to verify
|
||||||
# a kernel update or a newly added kernel got everything.
|
# a kernel update or a newly added kernel got everything.
|
||||||
local mark="${c_green}yes${c_reset}" miss="${c_red}no ${c_reset}" leds=false
|
local mark="${c_green}yes${c_reset}" miss="${c_red}no ${c_reset}" leds=false
|
||||||
detect_valve_fremont && command -v dkms >/dev/null 2>&1 && leds=true
|
detect_valve_fremont && command -v dkms >/dev/null 2>&1 && leds=true
|
||||||
local kdir k pkg headers hid led running line
|
local kdir k pkg headers hid led off running line
|
||||||
local legend="> = running; controller = Steam controller driver"
|
local legend="> = running; controller = Steam controller driver"
|
||||||
[[ "$leds" == true ]] && legend+=", LEDs = LED bar driver built"
|
[[ "$leds" == true ]] && legend+=", LEDs = LED bar driver built, power-off = power-off fix built"
|
||||||
echo -e " ${c_bold}Kernels${c_reset} ($legend)"
|
echo -e " ${c_bold}Kernels${c_reset} ($legend)"
|
||||||
for kdir in /usr/lib/modules/*/; do
|
for kdir in /usr/lib/modules/*/; do
|
||||||
k="$(basename "$kdir")"
|
k="$(basename "$kdir")"
|
||||||
@@ -352,7 +355,8 @@ kernel_overview() {
|
|||||||
line="$(printf '%-23s %-18s headers %b controller %b' "$k" "$pkg" "$headers" "$hid")"
|
line="$(printf '%-23s %-18s headers %b controller %b' "$k" "$pkg" "$headers" "$hid")"
|
||||||
if [[ "$leds" == true ]]; then
|
if [[ "$leds" == true ]]; then
|
||||||
led="$miss"; dkms status -k "$k" leds-valve-dkms 2>/dev/null | grep -q installed && led="$mark"
|
led="$miss"; dkms status -k "$k" leds-valve-dkms 2>/dev/null | grep -q installed && led="$mark"
|
||||||
line+="$(printf ' LEDs %b' "$led")"
|
off="$miss"; dkms status -k "$k" "$POWEROFF_DKMS_NAME" 2>/dev/null | grep -q installed && off="$mark"
|
||||||
|
line+="$(printf ' LEDs %b power-off %b' "$led" "$off")"
|
||||||
fi
|
fi
|
||||||
echo -e " ${running}${line}"
|
echo -e " ${running}${line}"
|
||||||
done
|
done
|
||||||
|
|||||||
@@ -0,0 +1,10 @@
|
|||||||
|
# Patches and extra sources
|
||||||
|
|
||||||
|
Files Steamify builds or applies, kept out of the shell code so they can be
|
||||||
|
read and reviewed on their own. `lib/*.sh` reads them with `patch_file
|
||||||
|
<name>`; the single-file build (`.github/tools/bundle.sh`) embeds them.
|
||||||
|
|
||||||
|
| File | Used by | What |
|
||||||
|
|---|---|---|
|
||||||
|
| `steamify-fremont-poweroff.c` | `lib/fremont-poweroff.sh` (Steam Machine support) | Kernel module (DKMS): clears GPIO 18's S4/S5 wake bit before power-off, so the Steam Machine stays off with recent kernels |
|
||||||
|
| `cros-ec-cec-single-port.patch` | `lib/cec.sh` (HDMI-CEC) | Makes Valve's Steam Machine CEC driver find amdgpu's HDMI port |
|
||||||
@@ -0,0 +1,21 @@
|
|||||||
|
Steam Machine CEC: find amdgpu's HDMI port with a single CEC port.
|
||||||
|
|
||||||
|
amdgpu registers its HDMI notifier without a port name, which only matches
|
||||||
|
the driver's named lookup ("Port C") if the CEC driver loaded first; amdgpu
|
||||||
|
loads from the initramfs, so it never does and /dev/cec0 stays at f.f.f.f.
|
||||||
|
With a single CEC port, look the notifier up by device alone.
|
||||||
|
|
||||||
|
Applies to Valve's drivers/media/cec/platform/cros-ec/cros-ec-cec.c
|
||||||
|
(evlaV/linux-integration 10c8c880, see CEC_DRIVER_URL in lib/cec.sh).
|
||||||
|
|
||||||
|
--- a/cros-ec-cec.c
|
||||||
|
+++ b/cros-ec-cec.c
|
||||||
|
@@ -485,7 +485,7 @@
|
||||||
|
goto out_probe_adapter;
|
||||||
|
}
|
||||||
|
|
||||||
|
- port->notify = cec_notifier_cec_adap_register(hdmi_dev, conns[port_num],
|
||||||
|
+ port->notify = cec_notifier_cec_adap_register(hdmi_dev, conns[1] ? conns[port_num] : NULL,
|
||||||
|
port->adap);
|
||||||
|
if (!port->notify) {
|
||||||
|
ret = -ENOMEM;
|
||||||
@@ -0,0 +1,80 @@
|
|||||||
|
// SPDX-License-Identifier: GPL-2.0
|
||||||
|
/*
|
||||||
|
* Steam Machine (Fremont): power off instead of booting up again.
|
||||||
|
*
|
||||||
|
* Since "pinctrl-amd: Don't clear S4 wake bits at probe" (Linux 7.2, also
|
||||||
|
* backported to 6.x, 7.0 and 7.1 updates) the S4/S5 wake bit the firmware
|
||||||
|
* leaves set on GPIO pin 18 (_SB.PCI0.GPP6) stays set, and the machine starts again right after powering off.
|
||||||
|
* Valve's kernel clears that bit at probe on Fremont (not for upstream);
|
||||||
|
* this does the same right before power-off, for kernels without it.
|
||||||
|
*/
|
||||||
|
#include <linux/acpi.h>
|
||||||
|
#include <linux/bits.h>
|
||||||
|
#include <linux/dmi.h>
|
||||||
|
#include <linux/io.h>
|
||||||
|
#include <linux/module.h>
|
||||||
|
#include <linux/platform_device.h>
|
||||||
|
#include <linux/reboot.h>
|
||||||
|
|
||||||
|
#define FREMONT_GPP6_PIN 18
|
||||||
|
#define WAKE_CNTRL_OFF_S4 15
|
||||||
|
|
||||||
|
static void __iomem *pin_reg;
|
||||||
|
static struct sys_off_handler *handler;
|
||||||
|
|
||||||
|
static int fremont_poweroff_prepare(struct sys_off_data *data)
|
||||||
|
{
|
||||||
|
u32 v = readl(pin_reg);
|
||||||
|
|
||||||
|
if (v & BIT(WAKE_CNTRL_OFF_S4))
|
||||||
|
writel(v & ~BIT(WAKE_CNTRL_OFF_S4), pin_reg);
|
||||||
|
return NOTIFY_DONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __init fremont_poweroff_init(void)
|
||||||
|
{
|
||||||
|
struct acpi_device *adev;
|
||||||
|
struct device *dev;
|
||||||
|
struct resource *res;
|
||||||
|
u32 v;
|
||||||
|
|
||||||
|
if (!dmi_match(DMI_BOARD_NAME, "Fremont"))
|
||||||
|
return -ENODEV;
|
||||||
|
adev = acpi_dev_get_first_match_dev("AMDI0030", NULL, -1);
|
||||||
|
if (!adev)
|
||||||
|
return -ENODEV;
|
||||||
|
dev = bus_find_device_by_acpi_dev(&platform_bus_type, adev);
|
||||||
|
acpi_dev_put(adev);
|
||||||
|
if (!dev)
|
||||||
|
return -ENODEV;
|
||||||
|
res = platform_get_resource(to_platform_device(dev), IORESOURCE_MEM, 0);
|
||||||
|
put_device(dev);
|
||||||
|
if (!res || resource_size(res) < (FREMONT_GPP6_PIN + 1) * 4)
|
||||||
|
return -ENODEV;
|
||||||
|
/* pinctrl-amd owns the region; only this one register is touched. */
|
||||||
|
pin_reg = ioremap(res->start + FREMONT_GPP6_PIN * 4, 4);
|
||||||
|
if (!pin_reg)
|
||||||
|
return -ENOMEM;
|
||||||
|
v = readl(pin_reg);
|
||||||
|
pr_info("GPIO %d register 0x%08x, S4/S5 wake %s\n", FREMONT_GPP6_PIN, v,
|
||||||
|
v & BIT(WAKE_CNTRL_OFF_S4) ? "set (cleared at power-off)" : "clear");
|
||||||
|
handler = register_sys_off_handler(SYS_OFF_MODE_POWER_OFF_PREPARE,
|
||||||
|
SYS_OFF_PRIO_DEFAULT, fremont_poweroff_prepare, NULL);
|
||||||
|
if (IS_ERR(handler)) {
|
||||||
|
iounmap(pin_reg);
|
||||||
|
return PTR_ERR(handler);
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void __exit fremont_poweroff_exit(void)
|
||||||
|
{
|
||||||
|
unregister_sys_off_handler(handler);
|
||||||
|
iounmap(pin_reg);
|
||||||
|
}
|
||||||
|
|
||||||
|
module_init(fremont_poweroff_init);
|
||||||
|
module_exit(fremont_poweroff_exit);
|
||||||
|
MODULE_DESCRIPTION("Steam Machine: clear GPIO 18's S4 wake bit before power-off");
|
||||||
|
MODULE_LICENSE("GPL");
|
||||||
|
MODULE_ALIAS("dmi:*:rnFremont:*");
|
||||||
+3
-2
@@ -15,11 +15,11 @@
|
|||||||
set -uo pipefail
|
set -uo pipefail
|
||||||
|
|
||||||
# Release version, see CHANGELOG.md.
|
# Release version, see CHANGELOG.md.
|
||||||
VERSION=2.1.0
|
VERSION=2.2.0
|
||||||
|
|
||||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
|
||||||
for lib in common state packages login-manager single-user steam-desktop steam-machine hdmi-refresh cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut menu backend; do
|
for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff hdmi-refresh cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut 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
|
||||||
@@ -139,6 +139,7 @@ while true; do
|
|||||||
for c in "${TO_ENABLE[@]}"; do
|
for c in "${TO_ENABLE[@]}"; 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 [[ "${CURRENT[$c]}" == 1 ]]; then echo " - re-apply: ${LABEL[$c]}"; else echo " - turn on: ${LABEL[$c]}"; fi
|
elif [[ "${CURRENT[$c]}" == 1 ]]; then echo " - re-apply: ${LABEL[$c]}"; else echo " - turn on: ${LABEL[$c]}"; fi
|
||||||
done
|
done
|
||||||
ask_yn "Go ahead?" y || { info "Nothing changed."; continue; }
|
ask_yn "Go ahead?" y || { info "Nothing changed."; continue; }
|
||||||
|
|||||||
+26
-10
@@ -69,8 +69,11 @@ ApplicationWindow {
|
|||||||
machine: { label: "Steam Machine support", hint: "LED bar, fan and performance settings in Steam",
|
machine: { label: "Steam Machine support", hint: "LED bar, fan and performance settings in Steam",
|
||||||
body: "The front LED bar works, Steam's hardware settings work, and the power button puts it to sleep like a console.",
|
body: "The front LED bar works, Steam's hardware settings work, and the power button puts it to sleep like a console.",
|
||||||
changes: ["leds-valve driver for every kernel (DKMS)", "steamos-manager for Steam's settings", "Console-like power handling"] },
|
changes: ["leds-valve driver for every kernel (DKMS)", "steamos-manager for Steam's settings", "Console-like power handling"] },
|
||||||
kpin: { label: "Pin the kernel", hint: "Fixes rebooting after shutdown",
|
poweroff: { label: "Power-off fix", hint: "Stays off after shutting down",
|
||||||
body: "Newer CachyOS kernels make the Steam Machine reboot instead of shutting down. Untick once CachyOS fixes that.",
|
body: "With recent kernels the Steam Machine starts again right after shutting down: the firmware leaves a wake bit set, and newer kernels (7.2, and updates of 6.x, 7.0 and 7.1) no longer clear it. Valve's own kernel clears it; this small module does the same right before power-off.",
|
||||||
|
changes: ["steamify-fremont-poweroff module for each installed kernel (DKMS)", "Only on a Steam Machine, only touches that one wake bit", "Off: recent kernels may start it again after shutting down"] },
|
||||||
|
kpin: { label: "Pin the kernel", hint: "Untick for CachyOS's current kernel",
|
||||||
|
body: "Keeps the Steam Machine on CachyOS kernel 7.1.6. Untick it to go back to CachyOS's current kernel; HDMI refresh boost goes with it.",
|
||||||
changes: ["linux-cachyos from Steamify's release (signature checked)", "Kept in /var/cache/steamify/kernel", "Added to IgnorePkg"] },
|
changes: ["linux-cachyos from Steamify's release (signature checked)", "Kept in /var/cache/steamify/kernel", "Added to IgnorePkg"] },
|
||||||
hdmi: { label: "HDMI refresh boost", hint: "Higher refresh rates over HDMI",
|
hdmi: { label: "HDMI refresh boost", hint: "Higher refresh rates over HDMI",
|
||||||
body: "The pinned kernel keeps many HDMI displays at 60 Hz. Turning this on shows which refresh rates your display can run at the desktop resolution; you pick them, and each one is tried for 15 seconds so you can check the picture before it's installed.",
|
body: "The pinned kernel keeps many HDMI displays at 60 Hz. Turning this on shows which refresh rates your display can run at the desktop resolution; you pick them, and each one is tried for 15 seconds so you can check the picture before it's installed.",
|
||||||
@@ -247,7 +250,9 @@ ApplicationWindow {
|
|||||||
if (id === "hdmi" && !hdmiChoice && hdmiSaved.length) { openHdmiList(); return; }
|
if (id === "hdmi" && !hdmiChoice && hdmiSaved.length) { openHdmiList(); return; }
|
||||||
if (id === "hdmi" && w.hdmi && !nowOn("hdmi") && !hdmiChoice) { startHdmi(); return; }
|
if (id === "hdmi" && w.hdmi && !nowOn("hdmi") && !hdmiChoice) { startHdmi(); return; }
|
||||||
if (id === "hdmi" && !w.hdmi) hdmiChoice = "";
|
if (id === "hdmi" && !w.hdmi) hdmiChoice = "";
|
||||||
if (id === "machine") w.kpin = w.machine;
|
if (id === "machine") w.poweroff = w.machine;
|
||||||
|
if (id === "poweroff" && w.poweroff) w.machine = true;
|
||||||
|
if (id === "machine" && !w.machine) w.kpin = false;
|
||||||
if (id === "kpin" && w.kpin) w.machine = true;
|
if (id === "kpin" && w.kpin) w.machine = true;
|
||||||
if (id === "hdmi" && w.hdmi) { w.machine = true; w.kpin = true; }
|
if (id === "hdmi" && w.hdmi) { w.machine = true; w.kpin = true; }
|
||||||
if (!w.machine || !w.kpin) w.hdmi = false;
|
if (!w.machine || !w.kpin) w.hdmi = false;
|
||||||
@@ -261,7 +266,7 @@ ApplicationWindow {
|
|||||||
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" });
|
if (want[it.id] && !it.on) p.push({ id: it.id, action: "on" });
|
||||||
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" });
|
||||||
else if (want[it.id] && reapply) p.push({ id: it.id, action: "again" });
|
else if (want[it.id] && (reapply || it.update)) p.push({ id: it.id, action: reapply ? "again" : "update" });
|
||||||
}
|
}
|
||||||
var bootNow = nowOn("boot") ? "desktop" : "gamescope";
|
var bootNow = nowOn("boot") ? "desktop" : "gamescope";
|
||||||
if (want.gaming && boot !== bootNow) p.push({ id: "boot", action: boot === "desktop" ? "desktop" : "gaming" });
|
if (want.gaming && boot !== bootNow) p.push({ id: "boot", action: boot === "desktop" ? "desktop" : "gaming" });
|
||||||
@@ -622,7 +627,11 @@ ApplicationWindow {
|
|||||||
x: row.modelData.parent ? 46 : 16
|
x: row.modelData.parent ? 46 : 16
|
||||||
width: controls.x - x - 16
|
width: controls.x - x - 16
|
||||||
opacity: row.modelData.kind === "action" && !(bios && bios.selectable) ? 0.6 : 1
|
opacity: row.modelData.kind === "action" && !(bios && bios.selectable) ? 0.6 : 1
|
||||||
Text { text: label(row.modelData); color: t.textHi; font.family: t.body; font.pixelSize: 17; font.weight: Font.DemiBold; elide: Text.ElideRight; width: parent.width }
|
// The name, with an Update badge when a newer version of it will be applied.
|
||||||
|
Row { width: parent.width; spacing: 10
|
||||||
|
Text { text: label(row.modelData); color: t.textHi; font.family: t.body; font.pixelSize: 17; font.weight: Font.DemiBold; elide: Text.ElideRight
|
||||||
|
width: Math.min(implicitWidth, parent.width - (upd.visible ? upd.width + parent.spacing : 0)) }
|
||||||
|
Chip { id: upd; visible: !!row.modelData.update; text: "Update"; fg: t.warn; bgc: t.warnBg; height: 20; anchors.verticalCenter: parent.verticalCenter } }
|
||||||
Text { text: row.modelData.id === "bios" ? biosHint() : ((row.modelData.id === "hdmi" && hdmiRowHint()) || (texts[row.modelData.id] && texts[row.modelData.id].hint) || row.modelData.hint); color: t.mute; font.family: t.body; font.pixelSize: 13; elide: Text.ElideRight; width: parent.width }
|
Text { text: row.modelData.id === "bios" ? biosHint() : ((row.modelData.id === "hdmi" && hdmiRowHint()) || (texts[row.modelData.id] && texts[row.modelData.id].hint) || row.modelData.hint); color: t.mute; font.family: t.body; font.pixelSize: 13; elide: Text.ElideRight; width: parent.width }
|
||||||
}
|
}
|
||||||
// Right: every control ends on the same edge
|
// Right: every control ends on the same edge
|
||||||
@@ -748,7 +757,7 @@ ApplicationWindow {
|
|||||||
model: plan
|
model: plan
|
||||||
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded }
|
ScrollBar.vertical: ScrollBar { policy: ScrollBar.AsNeeded }
|
||||||
delegate: Rectangle { required property var modelData; width: 740; height: 56; radius: 12; color: t.card
|
delegate: Rectangle { required property var modelData; width: 740; height: 56; radius: 12; color: t.card
|
||||||
readonly property var st: ({ on: ["Turn on", t.good, t.goodBg], off: ["Turn off", t.bad, t.badBg], again: ["Re-apply", "#7cc4ff", "#1b2b40"],
|
readonly property var st: ({ on: ["Turn on", t.good, t.goodBg], off: ["Turn off", t.bad, t.badBg], again: ["Re-apply", "#7cc4ff", "#1b2b40"], update: ["Update", t.warn, t.warnBg],
|
||||||
desktop: ["Desktop", "#7cc4ff", "#1b2b40"], gaming: ["Gaming", "#7cc4ff", "#1b2b40"],
|
desktop: ["Desktop", "#7cc4ff", "#1b2b40"], gaming: ["Gaming", "#7cc4ff", "#1b2b40"],
|
||||||
check: ["Check", t.warn, t.warnBg], flash: ["Flash", t.bad, t.badBg] })[modelData.action] || ["", t.text, t.card]
|
check: ["Check", t.warn, t.warnBg], flash: ["Flash", t.bad, t.badBg] })[modelData.action] || ["", t.text, t.card]
|
||||||
Row { anchors.fill: parent; anchors.leftMargin: 18; spacing: 14
|
Row { anchors.fill: parent; anchors.leftMargin: 18; spacing: 14
|
||||||
@@ -761,10 +770,10 @@ ApplicationWindow {
|
|||||||
Text { anchors.verticalCenter: parent.verticalCenter; x: 18; text: "Your password is asked once. Changes to how the PC starts need a restart."; color: t.soft; font.family: t.body; font.pixelSize: 14 } }
|
Text { anchors.verticalCenter: parent.verticalCenter; x: 18; text: "Your password is asked once. Changes to how the PC starts need a restart."; color: t.soft; font.family: t.body; font.pixelSize: 14 } }
|
||||||
}
|
}
|
||||||
Rectangle {
|
Rectangle {
|
||||||
x: parent.width - 468; y: 32; width: 428; height: 210; radius: 16; color: t.card
|
x: parent.width - 468; y: 32; width: 428; height: 250; radius: 16; color: t.card
|
||||||
Column { anchors.fill: parent; anchors.margins: 24; spacing: 12
|
Column { anchors.fill: parent; anchors.margins: 24; spacing: 12
|
||||||
Text { text: "SUMMARY"; color: t.faint; font.family: t.body; font.pixelSize: 12; font.weight: Font.DemiBold; font.letterSpacing: 0.8 }
|
Text { text: "SUMMARY"; color: t.faint; font.family: t.body; font.pixelSize: 12; font.weight: Font.DemiBold; font.letterSpacing: 0.8 }
|
||||||
Repeater { model: [["Turn on", "on"], ["Re-apply", "again"], ["Turn off", "off"]]
|
Repeater { model: [["Turn on", "on"], ["Update", "update"], ["Re-apply", "again"], ["Turn off", "off"]]
|
||||||
Row { required property var modelData; width: 380
|
Row { required property var modelData; width: 380
|
||||||
Text { text: parent.modelData[0]; color: t.soft; font.family: t.body; font.pixelSize: 15; width: 300 }
|
Text { text: parent.modelData[0]; color: t.soft; font.family: t.body; font.pixelSize: 15; width: 300 }
|
||||||
Text { text: plan.filter(function (p) { return p.action === parent.modelData[1]; }).length; color: t.text; font.family: t.mono; font.pixelSize: 15; width: 80; horizontalAlignment: Text.AlignRight } } }
|
Text { text: plan.filter(function (p) { return p.action === parent.modelData[1]; }).length; color: t.text; font.family: t.mono; font.pixelSize: 15; width: 80; horizontalAlignment: Text.AlignRight } } }
|
||||||
@@ -849,8 +858,15 @@ ApplicationWindow {
|
|||||||
border.width: parent.parent.st === "wait" || parent.parent.st === "run" ? 2 : 0; border.color: parent.parent.st === "run" ? t.accent : "#343f50"
|
border.width: parent.parent.st === "wait" || parent.parent.st === "run" ? 2 : 0; border.color: parent.parent.st === "run" ? t.accent : "#343f50"
|
||||||
Text { anchors.centerIn: parent; text: parent.parent.parent.st === "ok" ? "✓" : (parent.parent.parent.st === "fail" ? "!" : ""); color: parent.parent.parent.st === "ok" ? t.good : t.bad; font.pixelSize: 13; font.weight: Font.Bold }
|
Text { anchors.centerIn: parent; text: parent.parent.parent.st === "ok" ? "✓" : (parent.parent.parent.st === "fail" ? "!" : ""); color: parent.parent.parent.st === "ok" ? t.good : t.bad; font.pixelSize: 13; font.weight: Font.Bold }
|
||||||
RotationAnimator on rotation { running: parent.parent.parent.st === "run"; from: 0; to: 360; duration: 1000; loops: Animation.Infinite } }
|
RotationAnimator on rotation { running: parent.parent.parent.st === "run"; from: 0; to: 360; duration: 1000; loops: Animation.Infinite } }
|
||||||
Text { text: ({ on: "Turn on ", off: "Turn off ", again: "Re-apply ", desktop: "Boot into ", gaming: "Boot into ", check: "Download and check the ", flash: "Hand to fwupd: the " })[parent.parent.modelData.action] + ((texts[parent.parent.modelData.id] || {}).label || parent.parent.modelData.id)
|
// The step; an update shows the name with an Update badge, like the menu.
|
||||||
color: parent.parent.st === "wait" ? t.faint : t.textHi; font.family: t.body; font.pixelSize: 16; font.weight: Font.DemiBold; anchors.verticalCenter: parent.verticalCenter; width: 380; elide: Text.ElideRight }
|
Item { width: 380; height: parent.height
|
||||||
|
readonly property var step: parent.parent.modelData
|
||||||
|
readonly property bool update: step.action === "update"
|
||||||
|
Row { anchors.verticalCenter: parent.verticalCenter; width: parent.width; spacing: 10
|
||||||
|
Text { text: (({ on: "Turn on ", off: "Turn off ", again: "Re-apply ", desktop: "Boot into ", gaming: "Boot into ", check: "Download and check the ", flash: "Hand to fwupd: the " })[parent.parent.step.action] || "") + ((texts[parent.parent.step.id] || {}).label || parent.parent.step.id)
|
||||||
|
color: parent.parent.parent.parent.st === "wait" ? t.faint : t.textHi; font.family: t.body; font.pixelSize: 16; font.weight: Font.DemiBold; anchors.verticalCenter: parent.verticalCenter; elide: Text.ElideRight
|
||||||
|
width: Math.min(implicitWidth, parent.width - (stepUpd.visible ? stepUpd.width + parent.spacing : 0)) }
|
||||||
|
Chip { id: stepUpd; visible: parent.parent.update; text: "Update"; fg: t.warn; bgc: t.warnBg; height: 20; anchors.verticalCenter: parent.verticalCenter } } }
|
||||||
Text { text: ({ wait: "Waiting", run: "Working…", ok: "Done", fail: "Problem" })[parent.parent.st]; color: parent.parent.st === "ok" ? t.good : (parent.parent.st === "fail" ? t.bad : "#b8c3d1"); font.family: t.body; font.pixelSize: 13; anchors.verticalCenter: parent.verticalCenter }
|
Text { text: ({ wait: "Waiting", run: "Working…", ok: "Done", fail: "Problem" })[parent.parent.st]; color: parent.parent.st === "ok" ? t.good : (parent.parent.st === "fail" ? t.bad : "#b8c3d1"); font.family: t.body; font.pixelSize: 13; anchors.verticalCenter: parent.verticalCenter }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -2,7 +2,7 @@
|
|||||||
# Steamify CachyOS - get and start the app (v2), for `curl | bash`:
|
# Steamify CachyOS - get and start the app (v2), for `curl | bash`:
|
||||||
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash
|
# curl -fsSL https://github.com/theupriser/steamify-cachyos/releases/latest/download/steamify-app.sh | bash
|
||||||
# Downloads the newest release's app package (steamify-app.tar.gz: the app,
|
# Downloads the newest release's app package (steamify-app.tar.gz: the app,
|
||||||
# steamify.sh and lib/) to ~/.local/share/steamify/app, installs PySide6 if
|
# steamify.sh, lib/ and patches/) to ~/.local/share/steamify/app, installs PySide6 if
|
||||||
# it's missing, adds a launcher entry, and starts the app.
|
# it's missing, adds a launcher entry, and starts the app.
|
||||||
# STEAMIFY_RELEASE=<tag> takes that release instead of the newest (the
|
# STEAMIFY_RELEASE=<tag> takes that release instead of the newest (the
|
||||||
# v2-ui-preview build's copy of this script sets it). STEAMIFY_BRANCH=<branch>
|
# v2-ui-preview build's copy of this script sets it). STEAMIFY_BRANCH=<branch>
|
||||||
|
|||||||
+34
-8
@@ -197,19 +197,19 @@ class Backend(QObject):
|
|||||||
p.start()
|
p.start()
|
||||||
|
|
||||||
def _make_askpass(self, password):
|
def _make_askpass(self, password):
|
||||||
"""A private folder with the password, a helper that prints it for
|
"""A private folder with a helper that `sudo -A` runs for the
|
||||||
`sudo -A`, and a `sudo` that always uses the helper (makepkg and
|
password, and a `sudo` that always uses it (makepkg and yay call
|
||||||
yay call plain sudo)."""
|
plain sudo). The password is never written to a file: the helper
|
||||||
|
reads it from a named pipe, and the app writes it into the pipe from
|
||||||
|
memory each time sudo asks, until _drop_askpass."""
|
||||||
base = os.environ.get("XDG_RUNTIME_DIR") or tempfile.gettempdir()
|
base = os.environ.get("XDG_RUNTIME_DIR") or tempfile.gettempdir()
|
||||||
d = tempfile.mkdtemp(prefix="steamify-", dir=base)
|
d = tempfile.mkdtemp(prefix="steamify-", dir=base)
|
||||||
os.chmod(d, stat.S_IRWXU)
|
os.chmod(d, stat.S_IRWXU)
|
||||||
secret = os.path.join(d, "secret")
|
pipe = os.path.join(d, "pipe")
|
||||||
fd = os.open(secret, os.O_WRONLY | os.O_CREAT | os.O_EXCL, 0o600)
|
os.mkfifo(pipe, 0o600)
|
||||||
with os.fdopen(fd, "w") as f:
|
|
||||||
f.write(password + "\n")
|
|
||||||
askpass = os.path.join(d, "askpass")
|
askpass = os.path.join(d, "askpass")
|
||||||
with open(askpass, "w") as f:
|
with open(askpass, "w") as f:
|
||||||
f.write("#!/bin/sh\nexec cat %s\n" % json.dumps(secret))
|
f.write("#!/bin/sh\nexec cat %s\n" % json.dumps(pipe))
|
||||||
os.chmod(askpass, 0o700)
|
os.chmod(askpass, 0o700)
|
||||||
bindir = os.path.join(d, "bin")
|
bindir = os.path.join(d, "bin")
|
||||||
os.mkdir(bindir)
|
os.mkdir(bindir)
|
||||||
@@ -217,9 +217,35 @@ class Backend(QObject):
|
|||||||
with open(wrapper, "w") as f:
|
with open(wrapper, "w") as f:
|
||||||
f.write("#!/bin/sh\nexec /usr/bin/sudo -A \"$@\"\n")
|
f.write("#!/bin/sh\nexec /usr/bin/sudo -A \"$@\"\n")
|
||||||
os.chmod(wrapper, 0o700)
|
os.chmod(wrapper, 0o700)
|
||||||
|
stop = threading.Event()
|
||||||
|
data = (password + "\n").encode()
|
||||||
|
|
||||||
|
def serve():
|
||||||
|
while not stop.is_set():
|
||||||
|
try:
|
||||||
|
fd = os.open(pipe, os.O_WRONLY) # waits for a reader
|
||||||
|
except OSError:
|
||||||
|
return
|
||||||
|
try:
|
||||||
|
if not stop.is_set():
|
||||||
|
os.write(fd, data)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
finally:
|
||||||
|
os.close(fd)
|
||||||
|
threading.Thread(target=serve, daemon=True).start()
|
||||||
|
self._askpass_stop = (stop, pipe)
|
||||||
return d, askpass, bindir
|
return d, askpass, bindir
|
||||||
|
|
||||||
def _drop_askpass(self):
|
def _drop_askpass(self):
|
||||||
|
if getattr(self, "_askpass_stop", None):
|
||||||
|
stop, pipe = self._askpass_stop
|
||||||
|
self._askpass_stop = None
|
||||||
|
stop.set()
|
||||||
|
try: # wakes the writer waiting for a reader, which then ends
|
||||||
|
os.close(os.open(pipe, os.O_RDONLY | os.O_NONBLOCK))
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
if self._secret_dir:
|
if self._secret_dir:
|
||||||
shutil.rmtree(self._secret_dir, ignore_errors=True)
|
shutil.rmtree(self._secret_dir, ignore_errors=True)
|
||||||
self._secret_dir = None
|
self._secret_dir = None
|
||||||
|
|||||||
Reference in new issue
Block a user