mirror of
https://github.com/theupriser/steamify-cachyos.git
synced 2026-10-13 20:35:40 +02:00
Compare commits
36
Commits
No files matched your search
@@ -0,0 +1,2 @@
|
||||
Not tested on hardware yet: the whole Steamify flow for the patched gamescope on NVIDIA (apply, reboot, session).
|
||||
Delete this file when it was, after asking the user (AGENTS.md).
|
||||
@@ -48,11 +48,15 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
|
||||
`<id>_available` is checked via `component_available` (e.g. `machine`
|
||||
only on Fremont). Turn-on order is `COMPONENTS` order, turn-off reverse;
|
||||
`gaming` must stay first. Dependencies live in `toggle_component`.
|
||||
- **NVIDIA** (`lib/nvidia.sh`): gamescope's session is broken on NVIDIA, so the SteamOS conversion (`gaming`, `boot`, `single`,
|
||||
`glyphs`) is hidden there (unless already on) and `nvidia` + its sub-option `bigpicture` ("Gaming on NVIDIA") replace it:
|
||||
Steam on the Plasma desktop, started at login. Never reintroduce a gamescope session for NVIDIA without re-testing on the
|
||||
hardware (notes: steamify-cachyos-dev, `nvidia/`). It can't be tested without the hardware: keep the branch behind
|
||||
`.no-release-yet` until it was.
|
||||
- **NVIDIA** (`lib/gamescope-nvidia.sh`, `lib/nvidia.sh`): since 2.12.0 the SteamOS conversion (`gaming` and its options) runs on
|
||||
NVIDIA PCs with a patched gamescope (GBM scan-out: gamescope#2309; Steam's UI kept at the display's size), downloaded as the
|
||||
newest release of theupriser/steamify-gamescope-nvidia, with the pinned package (`GSN_VERSION`, `GSN_SHA256`), the copy the ISO
|
||||
leaves, and a build here (`patches/gamescope-nvidia.PKGBUILD` + the two patches) as the fallback. "Gaming on NVIDIA" (`nvidia`,
|
||||
`bigpicture`, `nvsilent`) isn't offered any more (`nvidia_available`); turning the conversion on turns it off (`nvidia_swap_out`).
|
||||
Builds exist per CPU level (`gsn_cpu_variant`: znver4, v4, v3, generic; `GSN_SHA256` is a map; the installed one must pass `gsn_smoke_test`, else generic). A new pin = the release's tag + each variant's sha256 (from `SHA256SUMS`) in `lib/gamescope-nvidia.sh` and the PKGBUILD's pkgver/pkgrel, copied from the
|
||||
repository (the patches in `patches/` must stay identical to that repository's). Steam must keep running with `-steamos3`
|
||||
(without it games don't launch in gaming mode and the settings pages go): never "fix" the UI size by dropping flags or by
|
||||
`XWAYLAND_COUNT`. Tested on an RTX 5080 only; keep the branch behind `.no-release-yet` until the whole flow ran on hardware.
|
||||
- **Feature versions.** Set `FEATURE_VERSION[<id>]` (`lib/menu.sh`) to the new
|
||||
`VERSION` whenever what `<id>_enable` sets up changes (a new component gets
|
||||
one too): installs recorded with an older version
|
||||
@@ -156,9 +160,8 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine
|
||||
a warning when `VERSION` is a version that's already released (so a
|
||||
release branch that forgot its bump shows it on every PR).
|
||||
- A release that adds, removes or renames a menu row also retakes the
|
||||
README screenshots (`assets/screenshot-menu-steam-machine.png` and
|
||||
`assets/screenshot-menu-nvidia.png`, side by side in the README): the app from the branch on a Steam Machine and on a PC
|
||||
with an NVIDIA card, every row visible and each window only as tall as its options need, the header showing the new
|
||||
README screenshot (`assets/screenshot-menu-steam-machine.png`; NVIDIA PCs get the same menu, so there is no separate
|
||||
one): the app from the branch on a Steam Machine, every row visible and the window only as tall as its options need, the header showing the new
|
||||
`VERSION`, 1600 px wide. Size the window with a KWin script (match the `steamify-ui` class exactly: a title match also hits
|
||||
Konsole and editor windows) and crop the full-screen capture to the client rectangle; mind the display scale (the Steam
|
||||
Machine runs at 1.75).
|
||||
|
||||
@@ -5,6 +5,30 @@ All notable changes, per version and per commit. Versions follow
|
||||
`steamify.sh`. Versions before 0.7.0 were numbered afterwards,
|
||||
one per merged pull request.
|
||||
|
||||
## 2.12.0 - 2026-10-13
|
||||
|
||||
- **feat: setting up the SteamOS conversion on NVIDIA first checks that nothing blocks it (a pacman lock, less than 1.5 GB free), and a failed setup is remembered: a desktop notification now and a reminder in the menu until a setup works**
|
||||
- **docs: the README and TECHNICAL.md describe NVIDIA as it is now (the SteamOS conversion with the patched gamescope; "Gaming on NVIDIA" only as history); the NVIDIA menu screenshot is gone, NVIDIA PCs get the same menu**
|
||||
- **feat: the patched gamescope comes in one build per CPU level, like CachyOS's repositories (generic, x86-64-v3, v4, znver4): Steamify picks the best one this CPU can run (`gsn_cpu_variant`), falls back to generic, and starts the installed gamescope once headless (`gsn_smoke_test`): a build that doesn't start is replaced by the generic one. CI compiles each with an explicit `-march` (a `-march=native` build died with SIGILL on a Ryzen) and checks the binary for instructions beyond its level.**
|
||||
- **feat: on NVIDIA the gaming session runs on the display's own mode and Steam's interface at its size: a second gamescope patch (`gamescope-ui-native.patch`, release `3.16.31-3` of steamify-gamescope-nvidia, now the pin; built once per CPU level, see below) keeps Steam's UI X server at the output size when Steam (with `-steamos3`) asks for 1920x1080, and a wrapper (`gamescope-display-wrapper.sh`, set up by the conversion with a drop-in of `gamescope-session.service`) starts gamescope on the mode KDE has saved for the connected display, else the biggest mode at the highest refresh rate: before, gaming mode ran at the display's preferred mode (60 Hz on many monitors) with a 1080p Steam UI. Tested on an RTX 5080, 3440x1440@175: widescreen, sharp, a Proton game starts**
|
||||
|
||||
- **fix: on an NVIDIA PC that had single user mode on before the SteamOS conversion, "Return to Gaming Mode" and the boot choice work again: single user mode's desktop autologin (`zzz-steamify-autologin.conf`, read last) pinned the Plasma session; the conversion removes it, and turning the conversion off sets it up again**
|
||||
|
||||
- **fix: when an option fails, its sub-options are not applied any more (a failed conversion used to leave its "Boot into the desktop", silent start and icons on without it)**
|
||||
|
||||
- **fix: a PC that keeps `python-hidapi` (liquidctl needs it) can use the SteamOS conversion: jupiter-hw-support, which gamescope-session-cachyos depends on, wants `python-hid`, which conflicts with it and stopped the package install. Steamify builds and installs an empty placeholder package that provides `python-hid` first (`patches/python-hid-shim.PKGBUILD`); only the Steam Deck controller firmware updater uses it**
|
||||
|
||||
- **feat: the patched gamescope is downloaded as the newest release of steamify-gamescope-nvidia (checked against that release's `.sha256`), a newer one than the installed package shows the SteamOS conversion as an update, and what this Steamify was tested with is the fallback: the pinned package (sha256 in Steamify), the copy the ISO leaves, a build here; the repository's workflow builds each release in CachyOS's container**
|
||||
|
||||
- `124b424`, `f234c72` **fix: HDMI-CEC on the Steam Machine no longer needs GitHub: when downloading Valve's CEC driver fails, Steamify uses the copy it carries (`patches/cros-ec-cec.c`), checked against the same pinned sha256**
|
||||
|
||||
- `127b8cf`, `2811ecc`, `f3cd053`, `4325d80`, `03f9e87`, `5b5181f`, `06ffc8b`, `4d60dd2`, `0ba94b2` **feat: the SteamOS conversion is offered on NVIDIA PCs again (GTX 16 / RTX 20 series or newer: Turing and later, the cards NVIDIA's open kernel driver supports): it builds and installs a patched gamescope from the recipe and patch Steamify carries (`patches/`, prepared in [steamify-gamescope-nvidia](https://github.com/theupriser/steamify-gamescope-nvidia); gamescope 3.16.31, so a Steamify release builds the version it was tested with) (GBM-allocated scan-out buffers, NVIDIA's corrupted gaming-mode picture, gamescope#2309; a few minutes to build) and sets `gamescope_drm_gbm_scanout=1` for the session (`lib/gamescope-nvidia.sh`). It replaces "Gaming on NVIDIA", which is not offered any more (the kernel parameters, early modules, suspend memory and the 32-bit libraries come with the conversion on a supported card; a PC that had "Gaming on NVIDIA" on has it turned off when the conversion is turned on). Tested on an RTX 5080 (driver 615.78.08): Steam's gaming UI is clean with it and shows artifacts without. The package is pinned to one gamescope release: updating it is not automatic yet**
|
||||
|
||||
- `ce4bc67` **feat: "Gaming on NVIDIA" also keeps video memory over suspend (`NVreg_PreserveVideoMemoryAllocations=1` in `/etc/modprobe.d/90-steamify-nvidia.conf`, the initramfs is rebuilt), enables `nvidia-suspend`/`nvidia-resume`/`nvidia-hibernate`, and installs `lib32-nvidia-utils` and `lib32-libxkbcommon` (32-bit games, the 32-bit MangoHud preload) on a supported card; off removes only what Steamify added. Untested on hardware yet**
|
||||
|
||||
## 2.11.1 - 2026-10-02
|
||||
|
||||
- `d70aeb8` **docs: new README screenshots of the app (2.11 menu) on a Steam Machine and on an NVIDIA PC, and the README names the opt-in "Start Steam silently in desktop mode" and the icons as an option of the conversion**
|
||||
## 2.11.0 - 2026-10-01
|
||||
|
||||
- `2e51d1d`, `3494924`, `fbdad5a`, `079e725`, `188799e` **feat: "Start Steam silently" sub-option: under the SteamOS conversion (`silent`, opt-in now: it was always on before; an existing one is moved from its systemd unit to the autostart entry by the "update") and under Gaming on NVIDIA (`nvsilent`, opt-in, not together with Big Picture); the SteamOS one is called "Start Steam silently in desktop mode" and is always shown, also on a Steam Machine; it is Steam's own autostart entry (`~/.config/autostart/steam.desktop`), so Steam's setting "Run Steam when my computer starts" follows it**
|
||||
|
||||
@@ -1,14 +1,9 @@
|
||||
# Steamify CachyOS
|
||||
|
||||
The Steamify app's menu. On a Steam Machine (or any PC with an AMD or Intel
|
||||
graphics card) it offers the full SteamOS conversion, on the left. On a PC with
|
||||
an NVIDIA graphics card gamescope's gaming mode shows a corrupted picture, so the
|
||||
menu offers **Gaming on NVIDIA** instead (Steam on the desktop, started at login,
|
||||
optionally in Big Picture), on the right.
|
||||
The Steamify app's menu, on a Steam Machine. On a PC with an NVIDIA graphics card (GTX 16 / RTX 20 series or
|
||||
newer) the same SteamOS conversion is offered; Steamify installs a patched gamescope for it.
|
||||
|
||||
| On a Steam Machine | On a PC with an NVIDIA graphics card |
|
||||
|---|---|
|
||||
|  |  |
|
||||

|
||||
|
||||
Turn a CachyOS desktop PC into a SteamOS-style console: it boots straight
|
||||
into Steam's Big Picture (gamescope), and you can switch to the KDE Plasma
|
||||
@@ -27,20 +22,28 @@ Steam Machine.
|
||||
on the desktop too.
|
||||
- **Boot into: gamescope / desktop** - where the PC starts. Gaming mode is
|
||||
the default.
|
||||
- **Start Steam silently in desktop mode** (off by default) - Steam starts
|
||||
at login on the desktop, in the tray without a window. It is Steam's own
|
||||
autostart entry, so Steam's setting "Run Steam when my computer starts"
|
||||
follows it (the Linux Steam client has no silent option of its own).
|
||||
- Steam's **System** settings show the SteamOS release Steamify follows
|
||||
as OS Version (e.g. `steamos-3.9`), `steam-machine` as OS Codename and
|
||||
Steamify's version as OS Variant. The OS name stays CachyOS
|
||||
([details](TECHNICAL.md#what-steams-system-settings-show)).
|
||||
- **NVIDIA graphics cards:** gamescope's gaming mode shows a corrupted
|
||||
picture there (an open NVIDIA driver bug), so the conversion isn't
|
||||
offered. **Gaming on NVIDIA** takes its place: Steam on the desktop,
|
||||
installed if it's missing and started at login, in **Big Picture** or its
|
||||
normal window as you choose
|
||||
([details](TECHNICAL.md#gaming-on-nvidia)).
|
||||
- **NVIDIA graphics cards** (GTX 16 / RTX 20 series or newer): stock gamescope shows a corrupted
|
||||
picture on them (an open NVIDIA driver bug), so on an NVIDIA PC Steamify installs a **patched gamescope**
|
||||
(GBM scan-out, from [steamify-gamescope-nvidia](https://github.com/theupriser/steamify-gamescope-nvidia))
|
||||
and sets up the rest: the gaming session starts on the monitor's own resolution and refresh rate, with
|
||||
Steam's interface at that size. It is the same conversion as everywhere else, with one extra step.
|
||||
The newest patched build for your CPU is downloaded from GitHub (checked, with a built-in fallback if GitHub
|
||||
can't be reached), and a newer one shows up as an **Update** in the menu. Tested on an RTX 5080 only
|
||||
([details](TECHNICAL.md#nvidia-graphics-cards)).
|
||||
The old **Gaming on NVIDIA** option (Steam on the desktop) is gone: a PC that had it on is moved to the
|
||||
conversion, and keeps starting Steam in Big Picture if that was on.
|
||||
2. **SteamOS theme** - the Vapor look of SteamOS for the desktop, with
|
||||
**Add to Steam** in right-click menus
|
||||
([details](TECHNICAL.md#steamos-desktop-look)).
|
||||
3. **Steam Deck/Machine icons** - Steam Deck button icons in gaming mode.
|
||||
3. **Steam Deck/Machine icons** - Steam Deck button icons in gaming mode (an option of the SteamOS conversion).
|
||||
4. **Single user mode** - like SteamOS: never a login or lock screen, and no
|
||||
KDE wallet password prompts (e.g. from Brave). Typing a password with a
|
||||
controller is no fun
|
||||
@@ -51,7 +54,7 @@ Steam Machine.
|
||||
With **Add as non-Steam game** (on by default) it's in your Steam library
|
||||
too: started from Steam it gets your controller (a Steam Controller only
|
||||
talks to Steam), in gaming mode as well. Steam closes for a moment while
|
||||
it's added, so that's done from the desktop. Not offered on PCs with an NVIDIA graphics card, which have no gaming mode.
|
||||
it's added, so that's done from the desktop. Not offered on PCs with an NVIDIA graphics card yet (not tested there).
|
||||
6. **Update notifications** - on by default: a notification and a tray icon
|
||||
when there's a new Steamify, with Open Steamify and Skip this version. It
|
||||
never updates anything by itself
|
||||
@@ -183,6 +186,18 @@ Contributing (or an AI agent)? Read [AGENTS.md](AGENTS.md).
|
||||
|
||||
MIT - do whatever you want with it. See [LICENSE.md](LICENSE.md).
|
||||
|
||||
### Code from other projects
|
||||
|
||||
Some files in `patches/` are not MIT. They keep their own license, and each file says where it comes from:
|
||||
|
||||
| File | What | License and source |
|
||||
|---|---|---|
|
||||
| `steamify-fremont-poweroff.c` | Kernel module for the Steam Machine's power-off fix. It does what Valve's Steam Machine kernel does for this (clears the S4 wake bit of GPIO pin 18 at probe), but at power-off, for kernels without Valve's change. Valve's kernel: [evlaV/linux-integration](https://github.com/evlaV/linux-integration). | GPL-2.0, like the Linux kernel it is built into |
|
||||
| `cros-ec-cec.c` | Valve's Steam Machine HDMI-CEC driver (ChromeOS EC CEC, Copyright BayLibre; Valve's version of it), at a pinned commit of [evlaV/linux-integration](https://github.com/evlaV/linux-integration). Used when it can't be downloaded. | GPL-2.0+, unmodified (Steamify's `cros-ec-cec-single-port.patch` is applied when it is built) |
|
||||
| `gamescope-gbm-scanout.patch` | GBM scan-out for gamescope on NVIDIA: nine commits of [farnoy/gamescope](https://github.com/farnoy/gamescope) on [ValveSoftware/gamescope](https://github.com/ValveSoftware/gamescope). | gamescope's license (BSD-2-Clause) |
|
||||
| `gamescope-ui-native.patch` | Keeps Steam's interface at the display's size in gamescope on NVIDIA (a small patch after the one above). | gamescope's license (BSD-2-Clause) |
|
||||
| `gamescope-nvidia.PKGBUILD` | The recipe for the patched gamescope package, used only when it can't be downloaded. | MIT, like Steamify |
|
||||
|
||||
## Disclaimer
|
||||
|
||||
Steamify CachyOS is an unofficial community project. It is not affiliated
|
||||
|
||||
+71
-12
@@ -43,7 +43,7 @@ user wallet, with anything saved in it meanwhile, is kept as
|
||||
`kdewallet.kwl.steamify-single-user` and used again the next time it's on.
|
||||
Passwords aren't shared between the two wallets.
|
||||
|
||||
**Without the conversion (NVIDIA PCs, where gamescope's session is broken)** nothing else logs in by itself, so single user mode
|
||||
**Without the conversion** (any PC with it off; on NVIDIA that is before 2.12.0 or after turning it off) nothing else logs in by itself, so single user mode
|
||||
does that too: `single_login_enable` (`lib/single-user.sh`) switches to SDDM and writes
|
||||
`/etc/sddm.conf.d/zzz-steamify-autologin.conf` (`User=`, `Session=plasma.desktop`, `Relogin=true`; named to sort after
|
||||
`steam-set-session`'s `zz-steamos-autologin.conf`, which may still say gamescope from an earlier conversion), and drops any
|
||||
@@ -229,19 +229,77 @@ headers first. A PC that already has the AUR's `xone-dkms-git` keeps it: it prov
|
||||
`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`).
|
||||
|
||||
## Gaming on NVIDIA
|
||||
## NVIDIA graphics cards
|
||||
|
||||
gamescope's own gaming mode shows a corrupted picture on NVIDIA graphics cards: NVIDIA's open bug 5240452 (flicker and
|
||||
artifacts in the DRM session at modes above 2560x1440@120, seen up to driver 595 and on RTX 3070 Ti, 4090, 5090 and 5080;
|
||||
no fix known). The kernel parameters `nvidia-drm.modeset=1`/`fbdev=1` and loading the NVIDIA modules early don't change it
|
||||
(the driver already reports both `Y`), and gamescope started nested inside KDE artifacts too when KWin hands its fullscreen
|
||||
window straight to the display. What does work on an RTX 5080: Steam's Big Picture (`steam -gamepadui`) as a normal window of
|
||||
the Plasma desktop session, smooth and clean.
|
||||
**The problem.** gamescope's gaming mode shows a corrupted picture on NVIDIA graphics cards: NVIDIA's open bug 5240452 (flicker
|
||||
and artifacts in the DRM session at modes above 2560x1440@120, seen up to driver 595 and on RTX 3070 Ti, 4090, 5090 and 5080; also
|
||||
[gamescope#2309](https://github.com/ValveSoftware/gamescope/issues/2309)). The kernel parameters `nvidia-drm.modeset=1`/`fbdev=1`
|
||||
and loading the NVIDIA modules early don't change it, and gamescope nested inside KDE artifacts too.
|
||||
|
||||
So with an NVIDIA GPU (PCI vendor 0x10de, display class, its driver providing `nvidia_drm`: `nvidia_present`) the SteamOS
|
||||
conversion (`gaming`, and its options `boot`, `single`, `glyphs`) isn't offered, unless it is already on (so it can be turned
|
||||
off). `nvidia` ("Gaming on NVIDIA", `lib/nvidia.sh`) takes its place, hidden while the conversion is on (both would start
|
||||
Steam at login): it installs `steam` when missing (recorded in state `nvidia`, removed again only then) and enables the user
|
||||
**The fix.** gamescope with GBM-allocated scan-out buffers, opt-in through the environment variable
|
||||
`gamescope_drm_gbm_scanout=1`. The patch is nine commits of [farnoy/gamescope](https://github.com/farnoy/gamescope)
|
||||
(`nvidia-gbm-wayland`). A second small patch keeps Steam's UI server (Steam with `-steamos3` asks gamescope for 1920x1080) at the
|
||||
display's size. Both are built into one package, `steamify-gamescope-nvidia` (it provides and conflicts with `gamescope`), in
|
||||
[theupriser/steamify-gamescope-nvidia](https://github.com/theupriser/steamify-gamescope-nvidia): the patches, the PKGBUILD, and CI.
|
||||
Tested on an RTX 5080 (driver 615.78.08, 3440x1440@175): clean picture, widescreen and sharp Steam UI, Proton games start.
|
||||
Everything below is `lib/gamescope-nvidia.sh` unless named otherwise.
|
||||
|
||||
**What the SteamOS conversion does on an NVIDIA PC** (`gaming_enable` calls `gsn_enable`; `nvidia_present`: PCI vendor 0x10de,
|
||||
display class, its driver providing `nvidia_drm`; `nvidia_supported`: GTX 16 / RTX 20 series or newer, Turing and later):
|
||||
1. `gsn_preflight`: no pacman lock, at least 1.5 GB free on `/`. A failure stops before anything changes.
|
||||
2. `nvidia_swap_out`: turns "Gaming on NVIDIA" off if it was on (see below).
|
||||
3. On a supported card the kernel parameters, early modules, suspend memory and 32-bit libraries (`nvidia_kernel_enable`,
|
||||
`nvidia_extras_enable`, `lib/nvidia.sh`, described below).
|
||||
4. `gsn_install_package`: the patched gamescope (next paragraph).
|
||||
5. `gsn_env_enable`: `gamescope_drm_gbm_scanout=1` in `~/.config/gamescope-session/environment` and
|
||||
`~/.config/environment.d/99-steamify-gamescope-nvidia.conf`.
|
||||
6. `gsn_display_enable`: the display wrapper. `patches/gamescope-display-wrapper.sh` is installed as `gamescope` in Steamify's bin
|
||||
folder and put first in the gaming session's PATH with a drop-in of `gamescope-session.service` (`steamify-display.conf`). It
|
||||
starts gamescope on the mode KDE has saved for the connected display (`~/.config/kwinoutputconfig.json`, modes from
|
||||
`modetest`), else the biggest mode at the highest refresh rate; with several displays connected it passes nothing and gamescope
|
||||
chooses. `--print-args` shows what it would use.
|
||||
7. `single_login_release` (`lib/single-user.sh`): single user mode's `zzz-steamify-autologin.conf` is read last and would pin the
|
||||
Plasma session, so it goes (turning the conversion off sets it up again). `python_hid_shim_ensure` (`lib/packages.sh`): the
|
||||
conversion's dependency jupiter-hw-support wants `python-hid`, which conflicts with `python-hidapi` (liquidctl needs it): an
|
||||
empty package providing `python-hid` (`patches/python-hid-shim.PKGBUILD`) makes both work.
|
||||
Turning the conversion off removes the setting, the wrapper and the kernel settings, and keeps the packages. When the setup
|
||||
fails, `gsn_fail_mark` records it (state `gamescope_nvidia`, key `failed`) and sends a desktop notification; the menu reminds
|
||||
(`gsn_fail_note`) until a setup works. A failed option's sub-options aren't applied.
|
||||
|
||||
**Getting the package, and keeping it current.** gamescope changes every few weeks, so the package is downloaded, not carried.
|
||||
CI builds it in CachyOS's container, once per CPU level like CachyOS's repositories: generic, x86-64-v3, v4 and znver4, each with an
|
||||
explicit `-march` (a `-march=native` build died with SIGILL on a Ryzen) and checked for instructions beyond its level and for both
|
||||
patches. A release is `<gamescope version>-<package release>` (for example `3.16.31-3`) with `SHA256SUMS`. `gsn_cpu_variant`
|
||||
picks the build for this CPU (the dynamic loader's x86-64 level; AMD with `avx512_bf16` is znver4) and generic is the fallback.
|
||||
`gsn_fetch_package` tries, for the CPU's variant and then generic: the newest release on GitHub (if newer than the pin), the release
|
||||
Steamify was tested with (`GSN_VERSION`, hashes in `GSN_SHA256`: from GitHub, the cache, or the copy the ISO leaves in
|
||||
`/usr/local/share/steamify/gamescope`); only files with the expected sha256 count (newer releases: their `.sha256`). If nothing is
|
||||
reachable, `gsn_build_package` builds from `patches/gamescope-nvidia.PKGBUILD` and the two patches (`makepkg` as the user in
|
||||
`~/.cache/steamify`). `gsn_install_package` installs with `pacman -U --ask 4`; `gsn_smoke_test` then starts gamescope headless
|
||||
and installs the generic build instead if it doesn't start. Nothing else changes before a package is installed. A newer release than
|
||||
the installed package shows the conversion as an update (`gsn_update_pending`, looked up at most every six hours, quiet offline).
|
||||
A new pin = the release's tag and each variant's sha256 in `GSN_VERSION` and `GSN_SHA256` plus the PKGBUILD's pkgver/pkgrel; the patches in `patches/` must
|
||||
stay identical to that repository's. New gamescope versions: the repository's daily check bumps the version when the patch still
|
||||
applies, or opens an issue when it needs a rebase; a person merges and tags the release.
|
||||
|
||||
**What the kernel side does** (`lib/nvidia.sh`; a supported card only): `nvidia-drm.modeset=1 nvidia-drm.fbdev=1` on the kernel command
|
||||
line (Limine, systemd-boot or GRUB; the file is backed up first; on Limine into the existing `KERNEL_CMDLINE[default]="..."` or
|
||||
`+="..."` line, never an extra appended line, which ends up as text), and `/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf`
|
||||
(`MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)`), then the initramfs and boot entries are rebuilt and checked. The
|
||||
early-load drop-in is only there while every installed kernel has the NVIDIA modules (mkinitcpio fails on a missing module): a
|
||||
pacman hook (`/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook`) runs `/usr/local/libexec/steamify-nvidia-initramfs` at every
|
||||
kernel or driver change and decides again. `nvidia_extras_enable` adds `NVreg_PreserveVideoMemoryAllocations=1`
|
||||
(`/etc/modprobe.d/90-steamify-nvidia.conf`), enables `nvidia-suspend`/`resume`/`hibernate` and installs `lib32-nvidia-utils` and
|
||||
`lib32-libxkbcommon`. Takes effect at the next boot. The tests are in steamify-cachyos-dev: `tests/nvidia-test.sh` (a fake GPU, a
|
||||
stub `pacman` and `systemctl`, a temp home) and `tests/nvidia-hardware-test.sh` (a real NVIDIA PC).
|
||||
|
||||
**Limits.** Tested on one PC (RTX 5080, Ryzen). The "Add as non-Steam game" option isn't offered on NVIDIA PCs yet. Steam must keep
|
||||
running with `-steamos3` (the Display settings' "Maximum Game Resolution" exists only with it, and dropping it or setting
|
||||
`XWAYLAND_COUNT=1` stopped games from launching). If upstream fixes the bug, the patched package and this section go, and Steamify
|
||||
goes back to the stock gamescope.
|
||||
|
||||
**"Gaming on NVIDIA" (before 2.12.0).** With an NVIDIA GPU the SteamOS conversion (`gaming`, and its options `boot`, `single`, `glyphs`) wasn't offered, unless
|
||||
it was already on. `nvidia` ("Gaming on NVIDIA", `lib/nvidia.sh`) took its place (both would start Steam at login): it installs `steam` when missing (recorded in state `nvidia`, removed again only then) and enables the user
|
||||
unit `steamify-steam-autostart.service` (`services/`, `ExecStart=/usr/bin/steam @ARGS@`, Plasma only). Its sub-option
|
||||
`bigpicture` ("Steam starts in Big Picture", ticked along with it) rewrites the unit with `-gamepadui` and sets
|
||||
`ksmserverrc [General] loginMode=emptySession` (`kset`, undone with `krevert bigpicture`), so windows of the last session
|
||||
@@ -572,6 +630,7 @@ immediately, which can turn into a loop - see
|
||||
| `lib/update-notifier.sh` | Update notifications: the notifier from `patches/` and its user timer |
|
||||
| `lib/first-login.sh` | `--defaults` without a session (the Steam Machine ISO's installer): the one-time first-login step that sets up single user's launcher on Plasma's new layout and opens the app |
|
||||
| `lib/controllers.sh` | Extended controller support: Xbox wireless dongle (xone) and Xbox Bluetooth (xpadneo) from the CachyOS repo |
|
||||
| `lib/gamescope-nvidia.sh` | gamescope on NVIDIA: builds and installs the patched gamescope (GBM scan-out) from steamify-gamescope-nvidia and sets `gamescope_drm_gbm_scanout=1` for the SteamOS conversion |
|
||||
| `lib/nvidia.sh` | Gaming on NVIDIA: Steam on the desktop, started at login, optionally in Big Picture (replaces the SteamOS conversion there) |
|
||||
| `lib/vram-booster.sh` | VRAM booster (`dmemcg-booster`, `plasma-foreground-booster`) |
|
||||
| `services/` | The systemd units the scripts install (`service_file`, `@KEY@` placeholders); see its README |
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 225 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 465 KiB After Width: | Height: | Size: 322 KiB |
+6
-1
@@ -147,7 +147,6 @@ backend_apply() {
|
||||
for id in "${want[@]}"; do [[ "$id" == "$c" ]] && WANTED[$c]=1; done
|
||||
done
|
||||
# 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; }
|
||||
@@ -186,6 +185,12 @@ backend_apply() {
|
||||
if backend_run_component "$c" disable; then feature_record "$c" disable; else failed+=("$c"); fi
|
||||
done
|
||||
for c in "${TO_ENABLE[@]}"; do
|
||||
# A sub-option of something that just failed isn't applied: it would be left on without its parent.
|
||||
if [[ -n "${PARENT[$c]:-}" && " ${failed[*]:-} " == *" ${PARENT[$c]} "* ]]; then
|
||||
backend_event log "\"id\":$(json_str "$c"),\"line\":$(json_str "Skipped: ${LABEL[${PARENT[$c]}]%%:*} didn't work.")"
|
||||
failed+=("$c")
|
||||
continue
|
||||
fi
|
||||
if backend_run_component "$c" enable; then is_action "$c" || feature_record "$c" enable; else failed+=("$c"); fi
|
||||
done
|
||||
feature_record_unticked
|
||||
|
||||
+9
-4
@@ -73,11 +73,16 @@ cec_driver_enable() {
|
||||
cp "$CEC_DRIVER_CACHE" "$tmp/cros-ec-cec.c"
|
||||
else
|
||||
info "Downloading Valve's Steam Machine CEC driver..."
|
||||
if ! curl -fsL --retry 2 "$CEC_DRIVER_URL" -o "$tmp/cros-ec-cec.c" ||
|
||||
if ! curl -fsL --retry 2 --max-time 60 "$CEC_DRIVER_URL" -o "$tmp/cros-ec-cec.c" ||
|
||||
! echo "$CEC_DRIVER_SHA256 $tmp/cros-ec-cec.c" | sha256sum -c --quiet -; then
|
||||
rm -rf "$tmp"
|
||||
err "Downloading Valve's CEC driver failed (or its checksum didn't match)."
|
||||
return 1
|
||||
# GitHub is down, rate-limits us, or sent something else: Steamify carries the same file (checked the same way).
|
||||
warn "Downloading Valve's CEC driver failed (or its checksum didn't match): using the copy Steamify carries."
|
||||
patch_file cros-ec-cec.c > "$tmp/cros-ec-cec.c"
|
||||
if ! echo "$CEC_DRIVER_SHA256 $tmp/cros-ec-cec.c" | sha256sum -c --quiet -; then
|
||||
rm -rf "$tmp"
|
||||
err "Steamify's own copy of Valve's CEC driver doesn't match the pinned checksum (a new pin needs a new copy in patches/)."
|
||||
return 1
|
||||
fi
|
||||
fi
|
||||
# The name carries the checksum: a newer pin gets its own file, the old one goes.
|
||||
sudo find "${CEC_DRIVER_CACHE%/*}" -maxdepth 1 -name 'cros-ec-cec-*.c' ! -name "${CEC_DRIVER_CACHE##*/}" -delete 2>/dev/null
|
||||
|
||||
@@ -0,0 +1,308 @@
|
||||
#!/bin/bash
|
||||
# gamescope's own gaming mode on NVIDIA: a patched gamescope that allocates its scan-out buffers with GBM, which cures
|
||||
# the corrupted picture (https://github.com/ValveSoftware/gamescope/issues/2309). The patch is opt-in: gamescope reads
|
||||
# `gamescope_drm_gbm_scanout=1` from its environment, which is set for the gaming session (gamescope-session's
|
||||
# environment file, and environment.d).
|
||||
#
|
||||
# gamescope changes every few weeks, so the package is not carried but downloaded: the newest release of
|
||||
# https://github.com/theupriser/steamify-gamescope-nvidia (CI builds it in CachyOS's container, a person merges the
|
||||
# version bump), and a newer one than the installed package shows as an update of the SteamOS conversion
|
||||
# (`gsn_update_pending`). What this Steamify release was tested with is the fallback: the pinned package (sha256 below),
|
||||
# a copy that came with the ISO, and last a build here from the recipe and patch in patches/. Simple files that don't change
|
||||
# stay in patches/ and are used as they are.
|
||||
#
|
||||
# Used by the SteamOS conversion (`gaming_enable`/`gaming_disable`) on every NVIDIA PC (`nvidia_present`); the kernel
|
||||
# parameters and the rest of lib/nvidia.sh's setup only on a supported card (`nvidia_supported`): GTX 16 / RTX 20 series or
|
||||
# newer (Turing and later, what NVIDIA's open kernel driver supports). Tested on an RTX 5080 only.
|
||||
# Sourced by steamify.sh; not meant to be run on its own.
|
||||
|
||||
GSN_PKG=steamify-gamescope-nvidia
|
||||
GSN_RELEASES="${GSN_RELEASES:-https://github.com/theupriser/steamify-gamescope-nvidia/releases}"
|
||||
# The package this Steamify release was tested with (patches/gamescope-nvidia.PKGBUILD names the same version): the
|
||||
# fallback. A new pin: the release's tag, the package's sha256, and the PKGBUILD's pkgver/pkgrel.
|
||||
GSN_VERSION=3.16.31-3
|
||||
# One build per CPU level, like CachyOS's repositories: generic is the one every x86-64 PC can run.
|
||||
declare -A GSN_SHA256=(
|
||||
[generic]=4c7995694d2726c356785d9aec12a11c98419b4d2e139617f03bb46c758ced56
|
||||
[v3]=43c6928ca9fa67c063c4e1b993b686145e94e68de1bd46645782d3186779fa2d
|
||||
[v4]=b6d4621e4b98d9aa5c600fe9a5a1cae164fef6bbf93d259fb8a6af666b098f9f
|
||||
[znver4]=60f45da74a0b96d776219f6e6aa22c5d23e48b08233b3fa12b6c4d7aa6812dd9
|
||||
)
|
||||
# Where the ISO leaves a copy of the pinned package, for installs without GitHub.
|
||||
GSN_BACKUP_DIR="${GSN_BACKUP_DIR:-/usr/local/share/steamify/gamescope}"
|
||||
# How long a look at GitHub for a newer release counts (seconds), so opening the menu doesn't ask every time.
|
||||
GSN_CHECK_EVERY="${GSN_CHECK_EVERY:-21600}"
|
||||
GSN_ENV_LINE='gamescope_drm_gbm_scanout=1'
|
||||
GSN_ENV_D="${GSN_ENV_D:-$HOME/.config/environment.d/99-steamify-gamescope-nvidia.conf}"
|
||||
GSN_SESSION_ENV="${GSN_SESSION_ENV:-$HOME/.config/gamescope-session/environment}"
|
||||
|
||||
gsn_package_installed() { pacman -Q "$GSN_PKG" >/dev/null 2>&1; }
|
||||
|
||||
gsn_installed_version() { pacman -Q "$GSN_PKG" 2>/dev/null | awk '{print $2}'; }
|
||||
|
||||
gsn_env_set() { [[ -f "$GSN_ENV_D" ]] && grep -qsx "$GSN_ENV_LINE" "$GSN_SESSION_ENV"; }
|
||||
|
||||
gsn_status() { gsn_package_installed && gsn_env_set; }
|
||||
|
||||
gsn_newer() {
|
||||
# gsn_newer <a> <b>: version a is newer than b (pacman's own comparison).
|
||||
[[ "$(vercmp "$1" "$2" 2>/dev/null)" == 1 ]]
|
||||
}
|
||||
|
||||
gsn_latest_tag() {
|
||||
# The newest release's tag (<gamescope version>-<package release>), from GitHub; nothing when it can't be reached or
|
||||
# there is no release. Follows the redirect of /releases/latest: no API, no rate limit, no JSON. gsn_latest_tag [seconds]
|
||||
local url
|
||||
url="$(curl -fsL --connect-timeout 4 --max-time "${1:-10}" -o /dev/null -w '%{url_effective}' "$GSN_RELEASES/latest" 2>/dev/null)" || return 1
|
||||
url="${url##*/}"
|
||||
[[ "$url" =~ ^[0-9][0-9A-Za-z._]*-[0-9]+$ ]] || return 1
|
||||
echo "$url"
|
||||
}
|
||||
|
||||
gsn_update_pending() {
|
||||
# The patched gamescope is installed and GitHub has a newer release: the menu shows the conversion as an update.
|
||||
# Looked up at most every GSN_CHECK_EVERY seconds (the answer is kept in the state), quickly, and silently when
|
||||
# offline: no network is never a reason to show or hide anything.
|
||||
gsn_package_installed || return 1
|
||||
local now checked latest
|
||||
now="$(date +%s)"; checked="$(state_get gamescope_nvidia checked 0)"
|
||||
if (( now - ${checked:-0} > GSN_CHECK_EVERY )); then
|
||||
state_set gamescope_nvidia checked "$now"
|
||||
latest="$(gsn_latest_tag 5)" && state_set gamescope_nvidia latest "$latest"
|
||||
fi
|
||||
latest="$(state_get gamescope_nvidia latest)"
|
||||
[[ -n "$latest" ]] && gsn_newer "$latest" "$(gsn_installed_version)"
|
||||
}
|
||||
|
||||
gsn_cpu_variant() {
|
||||
# The best build this CPU can run: znver4 (Ryzen 7000/9000: AMD with AVX-512 BF16), v4, v3, else generic. The
|
||||
# dynamic loader knows the CPU level best ("x86-64-v4 (supported, searched)").
|
||||
local help; help="$(/lib64/ld-linux-x86-64.so.2 --help 2>/dev/null)"
|
||||
if grep -q 'x86-64-v4 (supported' <<< "$help"; then
|
||||
if grep -q AuthenticAMD /proc/cpuinfo && grep -qw avx512_bf16 /proc/cpuinfo; then echo znver4; else echo v4; fi
|
||||
elif grep -q 'x86-64-v3 (supported' <<< "$help"; then echo v3
|
||||
else echo generic; fi
|
||||
}
|
||||
|
||||
gsn_sha_of() {
|
||||
# The hash a release file is expected to have: the pinned one for the pinned version, else the .sha256 next to the
|
||||
# package in the same release (it shows the download wasn't damaged, not who made it). gsn_sha_of <tag> <file> <variant>
|
||||
local tag="$1" file="$2" variant="$3"
|
||||
if [[ "$tag" == "$GSN_VERSION" ]]; then echo "${GSN_SHA256[$variant]}"; return 0; fi
|
||||
curl -fsL --connect-timeout 10 --max-time 30 "$GSN_RELEASES/download/$tag/$file.sha256" 2>/dev/null | awk 'NR==1 {print $1}'
|
||||
}
|
||||
|
||||
gsn_download() {
|
||||
# gsn_download <tag> <variant>: the release's package, verified, in the cache; prints its path.
|
||||
local tag="$1" variant="$2" cache="${XDG_CACHE_HOME:-$HOME/.cache}/steamify" file want got
|
||||
file="$GSN_PKG-$tag-$variant.pkg.tar.zst"
|
||||
mkdir -p "$cache" || return 1
|
||||
want="$(gsn_sha_of "$tag" "$file" "$variant")"
|
||||
[[ "$want" =~ ^[0-9a-f]{64}$ ]] || return 1
|
||||
if [[ -f "$cache/$file" ]] && [[ "$(sha256sum "$cache/$file" | awk '{print $1}')" == "$want" ]]; then echo "$cache/$file"; return 0; fi
|
||||
curl -fsL --retry 2 --connect-timeout 15 --max-time 300 -o "$cache/$file.part" "$GSN_RELEASES/download/$tag/$file" || { rm -f "$cache/$file.part"; return 1; }
|
||||
got="$(sha256sum "$cache/$file.part" | awk '{print $1}')"
|
||||
if [[ "$got" != "$want" ]]; then rm -f "$cache/$file.part"; return 1; fi
|
||||
mv -f "$cache/$file.part" "$cache/$file"
|
||||
echo "$cache/$file"
|
||||
}
|
||||
|
||||
gsn_fetch_package() {
|
||||
# The package to install, as a path: the build for this CPU, then the generic one, each from the newest release on
|
||||
# GitHub (not older than the pinned one), else the pinned release (GitHub, the cache, or the copy that came with the
|
||||
# ISO). Only files with the expected sha256 count. gsn_fetch_package [variant]
|
||||
local tag pkg v cache="${XDG_CACHE_HOME:-$HOME/.cache}/steamify" file copy
|
||||
local -a variants=("${1:-$(gsn_cpu_variant)}"); [[ "${variants[0]}" == generic ]] || variants+=(generic)
|
||||
tag="$(gsn_latest_tag)" || tag=""
|
||||
for v in "${variants[@]}"; do
|
||||
if [[ -n "$tag" && "$tag" != "$GSN_VERSION" ]] && gsn_newer "$tag" "$GSN_VERSION"; then
|
||||
info "Downloading the newest patched gamescope ($tag, $v)..." >&2
|
||||
if pkg="$(gsn_download "$tag" "$v")"; then echo "$pkg"; return 0; fi
|
||||
warn "Downloading the newest patched gamescope ($tag, $v) failed: trying the version this Steamify was tested with ($GSN_VERSION)." >&2
|
||||
fi
|
||||
info "Downloading the patched gamescope ($GSN_VERSION, $v)..." >&2
|
||||
if pkg="$(gsn_download "$GSN_VERSION" "$v")"; then echo "$pkg"; return 0; fi
|
||||
file="$GSN_PKG-$GSN_VERSION-$v.pkg.tar.zst"
|
||||
for copy in "$GSN_BACKUP_DIR/$file" "$cache/$file"; do
|
||||
if [[ -f "$copy" ]] && [[ "$(sha256sum "$copy" | awk '{print $1}')" == "${GSN_SHA256[$v]}" ]]; then
|
||||
info "Using the copy that came with Steamify: $copy" >&2
|
||||
echo "$copy"; return 0
|
||||
fi
|
||||
done
|
||||
done
|
||||
warn "Downloading the patched gamescope failed (or its checksum didn't match)." >&2
|
||||
return 1
|
||||
}
|
||||
|
||||
gsn_smoke_test() {
|
||||
# The installed gamescope must start (headless, no screen needed): a build that crashes would leave gaming mode in a loop.
|
||||
# It segfaults while shutting down headless (also unpatched builds), so what counts is that it ran the program.
|
||||
timeout 25 gamescope --backend headless -W 640 -H 360 -r 60 -- true 2>&1 | grep -q 'Primary child shut down'
|
||||
}
|
||||
|
||||
gsn_build_package() {
|
||||
# Last resort: builds the pinned package from the recipe and patch Steamify carries (patches/), as the user. It
|
||||
# downloads gamescope's sources (Valve's repository at the PKGBUILD's pkgver, and its submodules) and compiles them: a
|
||||
# few minutes. Prints the package's path.
|
||||
local dir cache="${XDG_CACHE_HOME:-$HOME/.cache}/steamify"
|
||||
info "Building the patched gamescope here instead (a few minutes)..." >&2
|
||||
sudo pacman -S --needed --noconfirm base-devel git >&2 || { err "Installing the build tools failed." >&2; return 1; }
|
||||
# On disk, not /tmp: the installer's chroot has a tmpfs there (RAM), and the build is 1-2 GB.
|
||||
mkdir -p "$cache" && dir="$(mktemp -d -p "$cache" gamescope-build.XXXXXX)" || return 1
|
||||
patch_file gamescope-nvidia.PKGBUILD > "$dir/PKGBUILD" &&
|
||||
patch_file gamescope-gbm-scanout.patch > "$dir/gamescope-gbm-scanout.patch" &&
|
||||
patch_file gamescope-ui-native.patch > "$dir/gamescope-ui-native.patch" || {
|
||||
err "Couldn't write the build files." >&2
|
||||
rm -rf "$dir"; return 1
|
||||
}
|
||||
if ! (cd "$dir" && makepkg -sf --noconfirm --nocheck >&2); then
|
||||
err "Building the patched gamescope failed (see above)." >&2
|
||||
rm -rf "$dir"; return 1
|
||||
fi
|
||||
# Moved out, so the build folder can go.
|
||||
local f built=""
|
||||
for f in "$dir"/"$GSN_PKG"-*.pkg.tar.*; do
|
||||
[[ -e "$f" && "$f" != *-debug-* ]] && { built="$f"; break; }
|
||||
done
|
||||
[[ -n "$built" ]] && mv "$built" "$cache/gamescope-local-build.pkg.tar.zst" || { rm -rf "$dir"; return 1; }
|
||||
rm -rf "$dir"
|
||||
echo "$cache/gamescope-local-build.pkg.tar.zst"
|
||||
}
|
||||
|
||||
gsn_install_package() {
|
||||
# The patched gamescope, newest first, each step only when the one before failed: the newest release from GitHub, the
|
||||
# pinned package (GitHub, cache or the copy that came with the ISO), a build here. A package that downloaded fine but
|
||||
# doesn't install (its libraries differ from this PC's) goes on to the next step too. Nothing else changes before one of
|
||||
# them is installed, so a failure leaves the PC as it was, with what it had. --ask 4 lets pacman replace the stock
|
||||
# gamescope it conflicts with, and an older patched one.
|
||||
local pkg latest have
|
||||
have="$(gsn_installed_version)"
|
||||
latest="$(gsn_latest_tag)" || latest=""
|
||||
# Already the newest (or, offline, the pinned version or newer): nothing to do.
|
||||
if [[ -n "$have" ]]; then
|
||||
if [[ -n "$latest" ]] && ! gsn_newer "$latest" "$have"; then return 0; fi
|
||||
if [[ -z "$latest" ]] && ! gsn_newer "$GSN_VERSION" "$have"; then return 0; fi
|
||||
fi
|
||||
if pkg="$(gsn_fetch_package)" && sudo pacman -U --noconfirm --ask 4 "$pkg"; then
|
||||
if ! gsn_smoke_test; then
|
||||
warn "The patched gamescope for this CPU doesn't start: installing the generic build instead."
|
||||
pkg="$(gsn_fetch_package generic)" && sudo pacman -U --noconfirm --ask 4 "$pkg" && gsn_smoke_test ||
|
||||
{ err "The patched gamescope doesn't start on this PC."; return 1; }
|
||||
fi
|
||||
ok "Patched gamescope installed."
|
||||
return 0
|
||||
fi
|
||||
warn "The ready-made patched gamescope couldn't be installed."
|
||||
[[ -n "$have" ]] && { warn "The installed one ($have) stays."; return 0; }
|
||||
if pkg="$(gsn_build_package)" && sudo pacman -U --noconfirm --ask 4 "$pkg"; then
|
||||
ok "Patched gamescope built and installed."
|
||||
return 0
|
||||
fi
|
||||
err "The patched gamescope couldn't be installed. The stock gamescope stays."
|
||||
return 1
|
||||
}
|
||||
|
||||
gsn_env_enable() {
|
||||
mkdir -p "$(dirname "$GSN_ENV_D")" "$(dirname "$GSN_SESSION_ENV")"
|
||||
echo "$GSN_ENV_LINE" > "$GSN_ENV_D"
|
||||
touch "$GSN_SESSION_ENV"
|
||||
grep -qsx "$GSN_ENV_LINE" "$GSN_SESSION_ENV" || echo "$GSN_ENV_LINE" >> "$GSN_SESSION_ENV"
|
||||
}
|
||||
|
||||
gsn_env_disable() {
|
||||
rm -f "$GSN_ENV_D"
|
||||
[[ -f "$GSN_SESSION_ENV" ]] && sed -i "/^${GSN_ENV_LINE}\$/d" "$GSN_SESSION_ENV"
|
||||
return 0
|
||||
}
|
||||
|
||||
# --- The session starts gamescope on the display's own mode
|
||||
#
|
||||
# gamescope-session starts gamescope without a size or a refresh rate, so it takes the display's *preferred* mode, which
|
||||
# many monitors declare as 60 Hz while the desktop runs them at their highest. patches/gamescope-display-wrapper.sh (put
|
||||
# first in the gaming session's PATH by a drop-in of gamescope-session.service) starts it on the mode KDE has saved for
|
||||
# the connected display, else the biggest mode at the highest refresh rate, read at every session start.
|
||||
GSN_DISPLAY_WRAPPER="${GSN_DISPLAY_WRAPPER:-$STEAMIFY_BIN/gamescope}"
|
||||
GSN_DISPLAY_DROPIN="${GSN_DISPLAY_DROPIN:-$HOME/.config/systemd/user/gamescope-session.service.d/steamify-display.conf}"
|
||||
|
||||
gsn_display_status() { [[ -x "$GSN_DISPLAY_WRAPPER" && -f "$GSN_DISPLAY_DROPIN" ]]; }
|
||||
|
||||
gsn_display_pending() {
|
||||
# The patched gamescope is in use but the session still starts it on the display's preferred mode: a normal run sets it up.
|
||||
gsn_package_installed && gsn_env_set && ! gsn_display_status
|
||||
}
|
||||
|
||||
gsn_display_enable() {
|
||||
patch_file gamescope-display-wrapper.sh | install_executable "$GSN_DISPLAY_WRAPPER" 755 || return 1
|
||||
mkdir -p "$(dirname "$GSN_DISPLAY_DROPIN")"
|
||||
cat > "$GSN_DISPLAY_DROPIN" << EOF
|
||||
# Steamify: the gaming session starts gamescope on the display's own mode (see lib/gamescope-nvidia.sh). Turning the
|
||||
# SteamOS conversion off removes this file.
|
||||
[Service]
|
||||
ExecStart=
|
||||
ExecStart=/usr/bin/bash -c 'PATH="$(dirname "$GSN_DISPLAY_WRAPPER"):\$PATH" exec /usr/lib/steamos/gamescope-session'
|
||||
EOF
|
||||
user_systemctl daemon-reload
|
||||
}
|
||||
|
||||
gsn_display_disable() {
|
||||
# Only our own files: the wrapper is recognised by its first comment line.
|
||||
grep -qs "^# Steamify: gamescope-session starts gamescope" "$GSN_DISPLAY_WRAPPER" && rm -f "$GSN_DISPLAY_WRAPPER"
|
||||
[[ -f "$GSN_DISPLAY_DROPIN" ]] && rm -f "$GSN_DISPLAY_DROPIN" && rmdir "$(dirname "$GSN_DISPLAY_DROPIN")" 2>/dev/null
|
||||
user_systemctl daemon-reload
|
||||
return 0
|
||||
}
|
||||
|
||||
gsn_preflight() {
|
||||
# Before anything changes: what would make the install fail half way. Prints the reason and fails, or passes.
|
||||
if [[ -e /var/lib/pacman/db.lck ]]; then
|
||||
echo "another package manager is running (/var/lib/pacman/db.lck): finish or close it and try again"; return 1
|
||||
fi
|
||||
local free; free="$(df --output=avail -BM / 2>/dev/null | tail -1 | tr -dc 0-9)"
|
||||
if [[ -n "$free" ]] && (( free < 1500 )); then
|
||||
echo "less than 1.5 GB free on /: free some space and try again"; return 1
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
gsn_fail_mark() {
|
||||
# Remember a failed setup so the menu says so on the next start, and tell the user now (desktop notification).
|
||||
state_set gamescope_nvidia failed "$(date '+%Y-%m-%d %H:%M'): $1"
|
||||
command -v notify-send >/dev/null 2>&1 &&
|
||||
notify-send -a Steamify -i dialog-warning "Steamify: gaming mode setup failed" "$1. Open Steamify and apply again." 2>/dev/null || true
|
||||
}
|
||||
|
||||
gsn_fail_note() {
|
||||
# The menu's reminder of a failed setup (nothing when the last one worked).
|
||||
local f; f="$(state_get gamescope_nvidia failed)"
|
||||
[[ -n "$f" ]] && warn "The last gaming mode setup for NVIDIA failed ($f). Apply the conversion again to retry."
|
||||
return 0
|
||||
}
|
||||
|
||||
gsn_enable() {
|
||||
# Only on a supported NVIDIA card; elsewhere the stock gamescope is right.
|
||||
nvidia_present || return 0
|
||||
local why
|
||||
if ! why="$(gsn_preflight)"; then err "Gaming mode for NVIDIA can't be set up: $why."; gsn_fail_mark "$why"; return 1; fi
|
||||
# A PC that had "Gaming on NVIDIA" on: Steam must not also start on the desktop.
|
||||
nvidia_swap_out
|
||||
# What NVIDIA's DRM stack wants (lib/nvidia.sh; a supported card only, the functions check): modeset/fbdev on the
|
||||
# kernel command line, the modules in the initramfs, video memory kept over suspend, the 32-bit libraries. Takes
|
||||
# effect at the next boot, like the session.
|
||||
nvidia_kernel_enable || return 1
|
||||
nvidia_extras_enable || return 1
|
||||
gsn_install_package || { gsn_fail_mark "the patched gamescope couldn't be installed"; return 1; }
|
||||
state_set gamescope_nvidia failed ""
|
||||
gsn_env_enable
|
||||
gsn_display_enable || warn "Couldn't set up the display mode for gaming mode: gamescope takes the display's preferred mode."
|
||||
ok "gamescope draws NVIDIA's picture through GBM (gamescope_drm_gbm_scanout=1) from the next gaming mode start."
|
||||
}
|
||||
|
||||
gsn_disable() {
|
||||
# The setting goes, and what nvidia_*_enable added; the packages stay (patched gamescope, 32-bit libraries), like
|
||||
# the other packages of the conversion (steam, gamescope-session-cachyos).
|
||||
gsn_env_disable
|
||||
gsn_display_disable
|
||||
nvidia_present || return 0
|
||||
nvidia_extras_disable
|
||||
nvidia_kernel_disable
|
||||
}
|
||||
@@ -94,6 +94,8 @@ gaming_enable() {
|
||||
# LOGIN_MANAGER (sddm with single user, else plasmalogin) is set by the
|
||||
# menu. Re-running with the other value switches over cleanly.
|
||||
install_required_packages || return 1
|
||||
# NVIDIA: gamescope's picture is corrupted unless it is the patched one (no-op elsewhere).
|
||||
gsn_enable || return 1
|
||||
case "$LOGIN_MANAGER" in
|
||||
sddm)
|
||||
switch_to_sddm || return 1
|
||||
@@ -107,6 +109,8 @@ gaming_enable() {
|
||||
install_session_sync
|
||||
;;
|
||||
esac
|
||||
# (NVIDIA with single user mode from before the conversion: its desktop autologin would pin the Plasma session.)
|
||||
single_login_release
|
||||
create_desktop_shortcut
|
||||
steam_enable
|
||||
os_name_enable || return 1
|
||||
@@ -122,8 +126,11 @@ gaming_disable() {
|
||||
switch_to_plasmalogin
|
||||
remove_desktop_shortcut
|
||||
steam_disable
|
||||
gsn_disable
|
||||
krevert gaming
|
||||
os_name_disable
|
||||
# NVIDIA with single user mode still on: nothing else logs in by itself any more.
|
||||
nvidia_present && single_status && single_login_enable
|
||||
ok "The PC boots to the normal login screen again (from the next boot)."
|
||||
}
|
||||
|
||||
|
||||
+25
-7
@@ -26,7 +26,7 @@ NO_PRESELECT=(boot silent nvsilent cec kpin hdmi extended_controller_support)
|
||||
# FEATURE_BASELINE.
|
||||
FEATURE_BASELINE=2.1.0
|
||||
declare -A FEATURE_VERSION=(
|
||||
[gaming]=2.9.0 [boot]=2.1.0 [silent]=2.11.0 [nvidia]=2.10.0 [bigpicture]=2.10.0 [nvsilent]=2.11.0 [theme]=2.1.0 [glyphs]=2.1.0 [single]=2.1.0
|
||||
[gaming]=2.9.0 [boot]=2.1.0 [silent]=2.11.0 [nvidia]=2.12.0 [bigpicture]=2.10.0 [nvsilent]=2.11.0 [theme]=2.1.0 [glyphs]=2.1.0 [single]=2.1.0
|
||||
[launcher]=2.1.0 [cec]=2.7.0 [machine]=2.9.0 [poweroff]=2.2.0 [vram]=2.3.0 [notify]=2.5.0 [steamgame]=2.5.1 [extended_controller_support]=2.10.0
|
||||
[kpin]=2.1.0 [hdmi]=2.1.0
|
||||
)
|
||||
@@ -57,11 +57,9 @@ declare -A CURRENT WANTED
|
||||
|
||||
component_available() {
|
||||
case "$1" in
|
||||
# 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).
|
||||
gaming|boot|silent|glyphs) ! nvidia_present || gaming_status ;;
|
||||
nvidia) nvidia_available ;;
|
||||
bigpicture|nvsilent) bigpicture_available ;;
|
||||
# The SteamOS conversion is offered on NVIDIA PCs too: it installs the patched gamescope there (lib/gamescope-nvidia.sh)
|
||||
# and turns off "Gaming on NVIDIA" (lib/nvidia.sh), which is not offered any more (nvidia_available).
|
||||
nvidia|bigpicture|nvsilent) nvidia_available ;;
|
||||
machine|poweroff) machine_available ;;
|
||||
vram) vram_available ;;
|
||||
# Steamify in the Steam library is for gaming mode's controller: not needed for Big Picture on the desktop (NVIDIA PCs;
|
||||
@@ -115,7 +113,11 @@ feature_outdated() {
|
||||
# On, but set up by an older version of that feature (compared as
|
||||
# versions: 2.10.0 is newer than 2.9.0).
|
||||
local have want
|
||||
# "Gaming on NVIDIA" is on: the SteamOS conversion replaces it, as an update of what the PC has (lib/nvidia.sh).
|
||||
[[ "$1" == gaming ]] && nvidia_migration_pending && return 0
|
||||
[[ "${CURRENT[$1]:-0}" == 1 ]] || return 1
|
||||
# NVIDIA: a newer patched gamescope is out (lib/gamescope-nvidia.sh): a normal run installs it.
|
||||
[[ "$1" == gaming ]] && nvidia_present && { gsn_update_pending || gsn_display_pending; } && return 0
|
||||
have="$(state_get features "$1" "$FEATURE_BASELINE")"
|
||||
[[ "$have" == off ]] && have="$FEATURE_BASELINE"
|
||||
want="${FEATURE_VERSION[$1]:-$FEATURE_BASELINE}"
|
||||
@@ -184,6 +186,17 @@ detect_components() {
|
||||
component_available "$c" || continue
|
||||
{ feature_outdated "$c" || feature_new "$c"; } && WANTED[$c]=1
|
||||
done
|
||||
# "Gaming on NVIDIA" was on: its replacement, the SteamOS conversion, comes with the defaults of a first run, and the
|
||||
# PC starts the way it did: Steam in Big Picture at login becomes booting into gaming mode, any other Steam start
|
||||
# (a normal window, or silent in the tray) becomes booting into the desktop, with the silent start kept.
|
||||
if nvidia_migration_pending; then
|
||||
WANTED[glyphs]=1
|
||||
if bigpicture_status; then WANTED[boot]=0
|
||||
else
|
||||
WANTED[boot]=1
|
||||
nvsilent_status && WANTED[silent]=1
|
||||
fi
|
||||
fi
|
||||
# The kernel pin is no longer needed (the power-off fix), and HDMI
|
||||
# refresh boost needed the pin: whatever is left of either is unticked,
|
||||
# so a normal run removes it.
|
||||
@@ -296,7 +309,6 @@ toggle_component() {
|
||||
component_selectable "$c" || return 1
|
||||
WANTED[$c]=$(( 1 - WANTED[$c] ))
|
||||
# Single user mode only makes sense on top of the SteamOS conversion.
|
||||
# (On NVIDIA the conversion isn't offered: single user mode logs in by itself, see lib/single-user.sh.)
|
||||
if [[ "$c" == single && "${WANTED[single]}" == 1 ]] && component_available gaming; then WANTED[gaming]=1; fi
|
||||
if [[ "$c" == gaming && "${WANTED[gaming]}" == 0 ]]; then WANTED[single]=0; WANTED[boot]=0; fi
|
||||
# Where to boot to is part of the conversion, too.
|
||||
@@ -325,6 +337,7 @@ toggle_component() {
|
||||
show_menu() {
|
||||
local i=0 c now want
|
||||
echo
|
||||
gsn_fail_note
|
||||
printf " ${c_bold}%-3s %-6s %-6s %s${c_reset}\n" "#" "Now" "Want" "Component"
|
||||
MENU_ITEMS=()
|
||||
for c in "${COMPONENTS[@]}"; do
|
||||
@@ -499,6 +512,11 @@ apply_changes() {
|
||||
if "${c}_disable"; then feature_record "$c" disable; else failed+=("$c"); fi
|
||||
done
|
||||
for c in "${TO_ENABLE[@]}"; do
|
||||
# A sub-option of something that just failed isn't applied: it would be left on without its parent.
|
||||
if [[ -n "${PARENT[$c]:-}" && " ${failed[*]:-} " == *" ${PARENT[$c]} "* ]]; then
|
||||
warn "Skipping ${LABEL[$c]%%:*}: ${LABEL[${PARENT[$c]}]%%:*} didn't work."
|
||||
continue
|
||||
fi
|
||||
if [[ "$c" == boot ]]; then echo; echo -e "${c_bold}Boot into: desktop${c_reset}"
|
||||
elif is_action "$c"; then echo; echo -e "${c_bold}Running: ${LABEL[$c]%%:*}${c_reset}"
|
||||
else echo; echo -e "${c_bold}Turning on: ${LABEL[$c]}${c_reset}"; fi
|
||||
|
||||
+77
-5
@@ -13,6 +13,11 @@ NVIDIA_INITRAMFS_CONF=${NVIDIA_INITRAMFS_CONF:-/etc/mkinitcpio.conf.d/90-steamif
|
||||
# The script and pacman hook that keep the initramfs file right at every kernel change.
|
||||
NVIDIA_SCRIPT=${NVIDIA_SCRIPT:-/usr/local/libexec/steamify-nvidia-initramfs}
|
||||
NVIDIA_HOOK=${NVIDIA_HOOK:-/etc/pacman.d/hooks/85-steamify-nvidia-initramfs.hook}
|
||||
# Keeps video memory over suspend (with the nvidia-suspend/resume services).
|
||||
NVIDIA_MODPROBE=${NVIDIA_MODPROBE:-/etc/modprobe.d/90-steamify-nvidia.conf}
|
||||
NVIDIA_SLEEP_UNITS="nvidia-suspend.service nvidia-resume.service nvidia-hibernate.service"
|
||||
# 32-bit parts Steam games and the 32-bit MangoHud preload need.
|
||||
NVIDIA_LIB32_PKGS="lib32-nvidia-utils lib32-libxkbcommon"
|
||||
# The running kernel's command line; the tests point this at a file.
|
||||
NVIDIA_CMDLINE=${NVIDIA_CMDLINE:-/proc/cmdline}
|
||||
# The DRM devices in sysfs; the tests point this at a fake tree.
|
||||
@@ -32,9 +37,27 @@ nvidia_present() {
|
||||
return 1
|
||||
}
|
||||
|
||||
# Shown on NVIDIA PCs, unless the SteamOS conversion is already on there (it
|
||||
# has to be turned off first: both would start Steam at login).
|
||||
nvidia_available() { nvidia_present && ! gaming_status 2>/dev/null; }
|
||||
# Not offered any more (2.12.0): the SteamOS conversion with the patched gamescope (lib/gamescope-nvidia.sh) covers NVIDIA PCs.
|
||||
# A PC that has this on keeps it until the conversion is turned on, which calls nvidia_swap_out.
|
||||
nvidia_available() { return 1; }
|
||||
|
||||
nvidia_migration_pending() {
|
||||
# "Gaming on NVIDIA" is on (set up before 2.12.0) and the SteamOS conversion that replaces it isn't: the menu ticks the
|
||||
# conversion and shows it as an update, so a normal run moves the PC over (nvidia_swap_out in gsn_enable).
|
||||
nvidia_present && nvidia_status && ! gaming_status 2>/dev/null
|
||||
}
|
||||
|
||||
nvidia_swap_out() {
|
||||
# The conversion takes over: Steam no longer starts by itself on the desktop at login (both would start it). Steam, the
|
||||
# packages and the NVIDIA settings stay: the conversion needs them too. The state stays for the conversion's own disable.
|
||||
nvidia_status || return 0
|
||||
info "Gaming on NVIDIA is replaced by the SteamOS conversion: Steam no longer starts on the desktop at login."
|
||||
user_systemctl disable --now "$NVIDIA_UNIT" >/dev/null 2>&1
|
||||
rm -f "$(nvidia_unit_file)"
|
||||
user_systemctl daemon-reload
|
||||
krevert bigpicture
|
||||
return 0
|
||||
}
|
||||
bigpicture_available() { nvidia_available; }
|
||||
|
||||
nvidia_status() {
|
||||
@@ -62,6 +85,7 @@ nvidia_enable() {
|
||||
nvidia_write_unit "$unit_args" || return 1
|
||||
user_systemctl enable "$NVIDIA_UNIT" >/dev/null 2>&1 || { err "Enabling $NVIDIA_UNIT failed."; return 1; }
|
||||
nvidia_kernel_enable || return 1
|
||||
nvidia_extras_enable || return 1
|
||||
ok "Steam starts at your next login."
|
||||
}
|
||||
|
||||
@@ -70,13 +94,19 @@ nvidia_disable() {
|
||||
user_systemctl disable --now "$NVIDIA_UNIT" >/dev/null 2>&1
|
||||
rm -f "$unit"
|
||||
user_systemctl daemon-reload
|
||||
# Swapped for the SteamOS conversion (turned on first, in the same run): it needs the same settings, they stay.
|
||||
gaming_status 2>/dev/null || nvidia_extras_disable
|
||||
pkgs="$(state_get nvidia installed_pkgs)"
|
||||
if gaming_status 2>/dev/null; then
|
||||
local p; pkgs=" $pkgs "
|
||||
for p in $NVIDIA_LIB32_PKGS; do pkgs="${pkgs// $p / }"; done
|
||||
fi
|
||||
# Only what Steamify installed; games and the Steam folder stay.
|
||||
# shellcheck disable=SC2086 # a list of package names
|
||||
[[ -n "$pkgs" ]] && sudo pacman -Rns --noconfirm $pkgs >/dev/null 2>&1
|
||||
state_clear nvidia
|
||||
krevert bigpicture
|
||||
nvidia_kernel_disable
|
||||
gaming_status 2>/dev/null || nvidia_kernel_disable
|
||||
ok "Steam no longer starts at login."
|
||||
}
|
||||
|
||||
@@ -118,7 +148,7 @@ nvsilent_disable() {
|
||||
|
||||
nvidia_supported() {
|
||||
# A supported NVIDIA GPU: PCI vendor 0x10de, display class, its kernel
|
||||
# driver installed, and not older than the RTX 20 series, decided like the
|
||||
# driver installed, and not older than Turing (GTX 16 / RTX 20 series: the cards NVIDIA's open kernel driver supports), decided like the
|
||||
# VRAM booster does (vram_nvidia_legacy_id: chwd's legacy card lists, every
|
||||
# other card runs NVIDIA's open modules). Older cards are left alone.
|
||||
local d
|
||||
@@ -254,6 +284,48 @@ nvidia_kernel_enable() {
|
||||
return 0
|
||||
}
|
||||
|
||||
nvidia_extras_enable() {
|
||||
# Video memory kept over sleep, the matching services and the 32-bit libraries (a supported card only).
|
||||
nvidia_supported || return 0
|
||||
local pkg u units="" prev missing=()
|
||||
if [[ ! -f "$NVIDIA_MODPROBE" ]]; then
|
||||
info "Keeping video memory over suspend (NVreg_PreserveVideoMemoryAllocations=1)..."
|
||||
printf '%s\n' '# Added by Steamify (Gaming on NVIDIA)' 'options nvidia NVreg_PreserveVideoMemoryAllocations=1' |
|
||||
sudo tee "$NVIDIA_MODPROBE" >/dev/null || { err "Writing $NVIDIA_MODPROBE failed."; return 1; }
|
||||
# The NVIDIA modules load from the initramfs, which carries modprobe.d.
|
||||
nvidia_rebuild_boot || warn "Rebuilding the initramfs failed: the suspend setting applies once it is rebuilt."
|
||||
fi
|
||||
for u in $NVIDIA_SLEEP_UNITS; do
|
||||
systemctl is-enabled -q "$u" 2>/dev/null && continue
|
||||
sudo systemctl enable "$u" >/dev/null 2>&1 && units+="${units:+ }$u"
|
||||
done
|
||||
if [[ -n "$units" ]]; then
|
||||
prev="$(state_get nvidia enabled_units)"
|
||||
state_set nvidia enabled_units "${prev:+$prev }$units"
|
||||
fi
|
||||
for pkg in $NVIDIA_LIB32_PKGS; do pacman -Q "$pkg" >/dev/null 2>&1 || missing+=("$pkg"); done
|
||||
if [[ ${#missing[@]} -gt 0 ]]; then
|
||||
info "Installing the 32-bit libraries games need: ${missing[*]}"
|
||||
if sudo pacman -S --needed --noconfirm "${missing[@]}"; then
|
||||
prev="$(state_get nvidia installed_pkgs)"
|
||||
state_set nvidia installed_pkgs "${prev:+$prev }${missing[*]}"
|
||||
else
|
||||
warn "Installing ${missing[*]} failed (is [multilib] enabled in /etc/pacman.conf?): 32-bit games may not start."
|
||||
fi
|
||||
fi
|
||||
return 0
|
||||
}
|
||||
|
||||
nvidia_extras_disable() {
|
||||
# Only what Steamify enabled; the 32-bit packages go with installed_pkgs.
|
||||
local u
|
||||
for u in $(state_get nvidia enabled_units); do sudo systemctl disable "$u" >/dev/null 2>&1; done
|
||||
if [[ -f "$NVIDIA_MODPROBE" ]]; then
|
||||
sudo rm -f "$NVIDIA_MODPROBE"
|
||||
nvidia_rebuild_boot || true
|
||||
fi
|
||||
}
|
||||
|
||||
nvidia_kernel_disable() {
|
||||
local f changed=0; f="$(nvidia_boot_file)"
|
||||
if [[ -n "$f" ]] && grep -q "$NVIDIA_PARAMS" "$f" 2>/dev/null; then
|
||||
|
||||
@@ -58,6 +58,30 @@ bootstrap_yay() {
|
||||
fi
|
||||
}
|
||||
|
||||
python_hid_shim_ensure() {
|
||||
# jupiter-hw-support (a dependency of gamescope-session-cachyos) wants python-hid, which conflicts with python-hidapi, and
|
||||
# a PC can need python-hidapi (liquidctl does). Only Valve's Steam Deck controller firmware updater uses python-hid, which
|
||||
# a PC doesn't run: so a placeholder package that provides python-hid keeps python-hidapi and satisfies the dependency
|
||||
# (also at later updates, which a one-time --assume-installed wouldn't). It stays when the conversion goes off:
|
||||
# jupiter-hw-support, which stays, depends on it.
|
||||
pacman -Q python-hidapi >/dev/null 2>&1 || return 0
|
||||
pacman -Q python-hid steamify-python-hid-shim >/dev/null 2>&1 && return 0
|
||||
local dir pkg cache="${XDG_CACHE_HOME:-$HOME/.cache}/steamify"
|
||||
warn "python-hidapi is installed (liquidctl and others need it) and conflicts with python-hid, which Steam Deck's tools ask for: keeping python-hidapi, with a small placeholder package for python-hid."
|
||||
sudo pacman -S --needed --noconfirm base-devel >/dev/null || { err "Installing the build tools failed."; return 1; }
|
||||
mkdir -p "$cache" && dir="$(mktemp -d -p "$cache" hid-shim.XXXXXX)" || return 1
|
||||
patch_file python-hid-shim.PKGBUILD > "$dir/PKGBUILD" || { rm -rf "$dir"; return 1; }
|
||||
if ! (cd "$dir" && makepkg -f --noconfirm >/dev/null) ||
|
||||
! pkg="$(ls "$dir"/steamify-python-hid-shim-*.pkg.tar.* 2>/dev/null | head -1)" ||
|
||||
! sudo pacman -U --noconfirm "$pkg"; then
|
||||
rm -rf "$dir"
|
||||
err "Couldn't set up the python-hid placeholder: python-hidapi and python-hid conflict, so the packages can't be installed."
|
||||
return 1
|
||||
fi
|
||||
rm -rf "$dir"
|
||||
ok "python-hidapi kept; python-hid is covered by a placeholder."
|
||||
}
|
||||
|
||||
install_required_packages() {
|
||||
# Step 2: gamescope/Steam and the desktop helpers the rest of the setup uses.
|
||||
local -a MISSING
|
||||
@@ -65,6 +89,7 @@ install_required_packages() {
|
||||
mapfile -t MISSING < <(pacman -T gamescope-session-cachyos steam mangohud xterm ttf-liberation wqy-zenhei plasma-keyboard)
|
||||
|
||||
if [[ ${#MISSING[@]} -gt 0 ]]; then
|
||||
python_hid_shim_ensure || return 1
|
||||
info "Installing missing packages: ${MISSING[*]}"
|
||||
sudo pacman -S --needed --noconfirm "${MISSING[@]}" || { err "Package installation failed."; return 1; }
|
||||
else
|
||||
|
||||
@@ -140,6 +140,15 @@ EOF
|
||||
remove_sddm_base_autologin
|
||||
}
|
||||
|
||||
single_login_release() {
|
||||
# The SteamOS conversion logs in by itself, and picks the session (gamescope, or the desktop with "Boot into"): the
|
||||
# desktop autologin above, which sorts last, would pin the Plasma session and "Return to Gaming Mode" would open KDE
|
||||
# again. Called when the conversion is turned on; turning it off sets it up again (gaming_disable).
|
||||
[[ -f "$SINGLE_AUTOLOGIN" ]] || return 0
|
||||
info "The SteamOS conversion logs in by itself: removing the desktop autologin of single user mode."
|
||||
sudo rm -f "$SINGLE_AUTOLOGIN"
|
||||
}
|
||||
|
||||
single_login_disable() {
|
||||
[[ -f "$SINGLE_AUTOLOGIN" ]] || return 0
|
||||
sudo rm -f "$SINGLE_AUTOLOGIN"
|
||||
|
||||
@@ -16,3 +16,9 @@ read and reviewed on their own. `lib/*.sh` reads them with `patch_file
|
||||
| `steamify-os-release.sh`, `steamify-os-release.hook` | `lib/login-manager.sh` (SteamOS conversion) | What Steam's System settings show besides the OS name (which stays CachyOS's): `VERSION_ID=steamos-X.Y`, `VERSION_CODENAME=steam-machine`, `VARIANT`/`VARIANT_ID` Steamify with its version, in `/etc/os-release`; the pacman hook runs it again after cachyos-hooks (`fill SCRIPT=... VERSION=... STEAMOS=...`) |
|
||||
| `steamify-nvidia-initramfs.sh`, `steamify-nvidia-initramfs.hook` | `lib/nvidia.sh` (SteamOS conversion, NVIDIA fix) | The pacman hook (before the initramfs is built, after DKMS) and its script: the NVIDIA modules go into the initramfs only while every installed kernel has them, decided again at every kernel or driver change (`fill SCRIPT=...`) |
|
||||
| `cros-ec-cec-single-port.patch` | `lib/cec.sh` (HDMI-CEC) | Makes Valve's Steam Machine CEC driver find amdgpu's HDMI port |
|
||||
| `gamescope-gbm-scanout.patch` | `lib/gamescope-nvidia.sh` (gamescope on NVIDIA) | gamescope 3.16.31 patch: scan-out buffers allocated with GBM (opt-in, `gamescope_drm_gbm_scanout=1`), which cures the corrupted picture on NVIDIA; from farnoy/gamescope, see the header of the file |
|
||||
| `gamescope-nvidia.PKGBUILD` | `lib/gamescope-nvidia.sh` (gamescope on NVIDIA) | The recipe for the patched gamescope package (CI builds it per CPU level; here it is only the local-build fallback) (`steamify-gamescope-nvidia`, provides and conflicts with `gamescope`): Valve's gamescope at `pkgver` plus `gamescope-gbm-scanout.patch`; written with the patch to a temporary folder and built with `makepkg` |
|
||||
| `cros-ec-cec.c` | `lib/cec.sh` (HDMI-CEC) | Valve's Steam Machine CEC driver (GPL-2.0+, from evlaV/linux-integration at the pinned commit): the backup when downloading it from GitHub fails; used only if its sha256 equals `CEC_DRIVER_SHA256` (a new pin needs a new copy) |
|
||||
| `python-hid-shim.PKGBUILD` | `lib/packages.sh` (required packages) | An empty package that provides `python-hid`, for PCs that keep `python-hidapi` (they conflict; jupiter-hw-support, a dependency of gamescope-session-cachyos, wants python-hid, only the Steam Deck controller updater uses it): built with `makepkg` and installed before the conversion's packages |
|
||||
| `gamescope-ui-native.patch` | `lib/gamescope-nvidia.sh` (gamescope on NVIDIA) | Small patch after the GBM one: keeps Steam's UI X server at the display's size when Steam (with `-steamos3`) asks for 1920x1080; `gamescope_xwayland_ui_native=0` turns it off |
|
||||
| `gamescope-display-wrapper.sh` | `lib/gamescope-nvidia.sh` (gamescope on NVIDIA) | Installed as `gamescope` in Steamify's bin folder and put first in the gaming session's PATH: starts gamescope on the mode KDE has saved for the connected display, else the biggest mode at the highest refresh rate (modetest + `kwinoutputconfig.json`) |
|
||||
@@ -0,0 +1,621 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* CEC driver for ChromeOS Embedded Controller
|
||||
*
|
||||
* Copyright (c) 2018 BayLibre, SAS
|
||||
* Author: Neil Armstrong <narmstrong@baylibre.com>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/dmi.h>
|
||||
#include <linux/pci.h>
|
||||
#include <linux/cec.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/platform_data/cros_ec_commands.h>
|
||||
#include <linux/platform_data/cros_ec_proto.h>
|
||||
#include <media/cec.h>
|
||||
#include <media/cec-notifier.h>
|
||||
|
||||
#define DRV_NAME "cros-ec-cec"
|
||||
|
||||
/**
|
||||
* struct cros_ec_cec_port - Driver data for a single EC CEC port
|
||||
*
|
||||
* @port_num: port number
|
||||
* @adap: CEC adapter
|
||||
* @notify: CEC notifier pointer
|
||||
* @rx_msg: storage for a received message
|
||||
* @cros_ec_cec: pointer to the parent struct
|
||||
*/
|
||||
struct cros_ec_cec_port {
|
||||
int port_num;
|
||||
struct cec_adapter *adap;
|
||||
struct cec_notifier *notify;
|
||||
struct cec_msg rx_msg;
|
||||
struct cros_ec_cec *cros_ec_cec;
|
||||
};
|
||||
|
||||
/**
|
||||
* struct cros_ec_cec - Driver data for EC CEC
|
||||
*
|
||||
* @cros_ec: Pointer to EC device
|
||||
* @notifier: Notifier info for responding to EC events
|
||||
* @write_cmd_version: Highest supported version of EC_CMD_CEC_WRITE_MSG.
|
||||
* @num_ports: Number of CEC ports
|
||||
* @ports: Array of ports
|
||||
*/
|
||||
struct cros_ec_cec {
|
||||
struct cros_ec_device *cros_ec;
|
||||
struct notifier_block notifier;
|
||||
int write_cmd_version;
|
||||
int num_ports;
|
||||
struct cros_ec_cec_port *ports[EC_CEC_MAX_PORTS];
|
||||
};
|
||||
|
||||
static void cros_ec_cec_received_message(struct cros_ec_cec_port *port,
|
||||
uint8_t *msg, uint8_t len)
|
||||
{
|
||||
if (len > CEC_MAX_MSG_SIZE)
|
||||
len = CEC_MAX_MSG_SIZE;
|
||||
|
||||
port->rx_msg.len = len;
|
||||
memcpy(port->rx_msg.msg, msg, len);
|
||||
|
||||
cec_received_msg(port->adap, &port->rx_msg);
|
||||
}
|
||||
|
||||
static void handle_cec_message(struct cros_ec_cec *cros_ec_cec)
|
||||
{
|
||||
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
|
||||
uint8_t *cec_message = cros_ec->event_data.data.cec_message;
|
||||
unsigned int len = cros_ec->event_size;
|
||||
struct cros_ec_cec_port *port;
|
||||
/*
|
||||
* There are two ways of receiving CEC messages:
|
||||
* 1. Old EC firmware which only supports one port sends the data in a
|
||||
* cec_message MKBP event.
|
||||
* 2. New EC firmware which supports multiple ports uses
|
||||
* EC_MKBP_CEC_HAVE_DATA to notify that data is ready and
|
||||
* EC_CMD_CEC_READ_MSG to read it.
|
||||
* Check that the EC only has one CEC port, and then we can assume the
|
||||
* message is from port 0.
|
||||
*/
|
||||
if (cros_ec_cec->num_ports != 1) {
|
||||
dev_err(cros_ec->dev,
|
||||
"received cec_message on device with %d ports\n",
|
||||
cros_ec_cec->num_ports);
|
||||
return;
|
||||
}
|
||||
port = cros_ec_cec->ports[0];
|
||||
|
||||
cros_ec_cec_received_message(port, cec_message, len);
|
||||
}
|
||||
|
||||
static void cros_ec_cec_read_message(struct cros_ec_cec_port *port)
|
||||
{
|
||||
struct cros_ec_device *cros_ec = port->cros_ec_cec->cros_ec;
|
||||
struct ec_params_cec_read params = {
|
||||
.port = port->port_num,
|
||||
};
|
||||
struct ec_response_cec_read response;
|
||||
int ret = 0;
|
||||
int count = 0;
|
||||
|
||||
/* NOTE read as many messages as we can, return value from cros_ec_cmd
|
||||
* here should either equal to the bytes transferred or a negative
|
||||
* error code, so as long as we get positive integers we are
|
||||
* reading messages!
|
||||
*/
|
||||
do {
|
||||
ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_READ_MSG, ¶ms,
|
||||
sizeof(params), &response, sizeof(response));
|
||||
if (ret > 0) {
|
||||
++count;
|
||||
cros_ec_cec_received_message(port, response.msg, response.msg_len);
|
||||
}
|
||||
}
|
||||
while (ret > 0);
|
||||
|
||||
if (ret < 0 && count == 0) {
|
||||
dev_err(cros_ec->dev,
|
||||
"error reading CEC message on EC: %d\n", ret);
|
||||
}
|
||||
}
|
||||
|
||||
static void handle_cec_event(struct cros_ec_cec *cros_ec_cec)
|
||||
{
|
||||
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
|
||||
uint32_t cec_events = cros_ec->event_data.data.cec_events;
|
||||
uint32_t port_num = EC_MKBP_EVENT_CEC_GET_PORT(cec_events);
|
||||
uint32_t events = EC_MKBP_EVENT_CEC_GET_EVENTS(cec_events);
|
||||
struct cros_ec_cec_port *port;
|
||||
|
||||
if (port_num >= cros_ec_cec->num_ports) {
|
||||
dev_err(cros_ec->dev,
|
||||
"received CEC event for invalid port %d\n", port_num);
|
||||
return;
|
||||
}
|
||||
port = cros_ec_cec->ports[port_num];
|
||||
|
||||
if (events & EC_MKBP_CEC_SEND_OK)
|
||||
cec_transmit_attempt_done(port->adap, CEC_TX_STATUS_OK);
|
||||
|
||||
/* FW takes care of all retries, tell core to avoid more retries */
|
||||
if (events & EC_MKBP_CEC_SEND_FAILED)
|
||||
cec_transmit_attempt_done(port->adap,
|
||||
CEC_TX_STATUS_MAX_RETRIES |
|
||||
CEC_TX_STATUS_NACK);
|
||||
|
||||
if (events & EC_MKBP_CEC_HAVE_DATA)
|
||||
cros_ec_cec_read_message(port);
|
||||
}
|
||||
|
||||
static int cros_ec_cec_event(struct notifier_block *nb,
|
||||
unsigned long queued_during_suspend,
|
||||
void *_notify)
|
||||
{
|
||||
struct cros_ec_cec *cros_ec_cec;
|
||||
struct cros_ec_device *cros_ec;
|
||||
|
||||
cros_ec_cec = container_of(nb, struct cros_ec_cec, notifier);
|
||||
cros_ec = cros_ec_cec->cros_ec;
|
||||
|
||||
if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_EVENT) {
|
||||
handle_cec_event(cros_ec_cec);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
if (cros_ec->event_data.event_type == EC_MKBP_EVENT_CEC_MESSAGE) {
|
||||
handle_cec_message(cros_ec_cec);
|
||||
return NOTIFY_OK;
|
||||
}
|
||||
|
||||
return NOTIFY_DONE;
|
||||
}
|
||||
|
||||
static int cros_ec_cec_set_log_addr(struct cec_adapter *adap, u8 logical_addr)
|
||||
{
|
||||
struct cros_ec_cec_port *port = adap->priv;
|
||||
struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
|
||||
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
|
||||
struct ec_params_cec_set params = {
|
||||
.cmd = CEC_CMD_LOGICAL_ADDRESS,
|
||||
.port = port->port_num,
|
||||
.val = logical_addr,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, ¶ms, sizeof(params),
|
||||
NULL, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(cros_ec->dev,
|
||||
"error setting CEC logical address on EC: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_cec_transmit(struct cec_adapter *adap, u8 attempts,
|
||||
u32 signal_free_time, struct cec_msg *cec_msg)
|
||||
{
|
||||
struct cros_ec_cec_port *port = adap->priv;
|
||||
struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
|
||||
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
|
||||
struct ec_params_cec_write params;
|
||||
struct ec_params_cec_write_v1 params_v1;
|
||||
int ret;
|
||||
|
||||
if (cros_ec_cec->write_cmd_version == 0) {
|
||||
memcpy(params.msg, cec_msg->msg, cec_msg->len);
|
||||
ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_WRITE_MSG, ¶ms,
|
||||
cec_msg->len, NULL, 0);
|
||||
} else {
|
||||
params_v1.port = port->port_num;
|
||||
params_v1.msg_len = cec_msg->len;
|
||||
memcpy(params_v1.msg, cec_msg->msg, cec_msg->len);
|
||||
ret = cros_ec_cmd(cros_ec, cros_ec_cec->write_cmd_version,
|
||||
EC_CMD_CEC_WRITE_MSG, ¶ms_v1,
|
||||
sizeof(params_v1), NULL, 0);
|
||||
}
|
||||
|
||||
if (ret < 0) {
|
||||
dev_err(cros_ec->dev,
|
||||
"error writing CEC msg on EC: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_cec_adap_enable(struct cec_adapter *adap, bool enable)
|
||||
{
|
||||
struct cros_ec_cec_port *port = adap->priv;
|
||||
struct cros_ec_cec *cros_ec_cec = port->cros_ec_cec;
|
||||
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
|
||||
struct ec_params_cec_set params = {
|
||||
.cmd = CEC_CMD_ENABLE,
|
||||
.port = port->port_num,
|
||||
.val = enable,
|
||||
};
|
||||
int ret;
|
||||
|
||||
ret = cros_ec_cmd(cros_ec, 0, EC_CMD_CEC_SET, ¶ms, sizeof(params),
|
||||
NULL, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(cros_ec->dev,
|
||||
"error %sabling CEC on EC: %d\n",
|
||||
(enable ? "en" : "dis"), ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct cec_adap_ops cros_ec_cec_ops = {
|
||||
.adap_enable = cros_ec_cec_adap_enable,
|
||||
.adap_log_addr = cros_ec_cec_set_log_addr,
|
||||
.adap_transmit = cros_ec_cec_transmit,
|
||||
};
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int cros_ec_cec_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(cros_ec_cec->cros_ec->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_cec_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct cros_ec_cec *cros_ec_cec = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(cros_ec_cec->cros_ec->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(cros_ec_cec_pm_ops,
|
||||
cros_ec_cec_suspend, cros_ec_cec_resume);
|
||||
|
||||
#if IS_ENABLED(CONFIG_PCI) && IS_ENABLED(CONFIG_DMI)
|
||||
|
||||
/*
|
||||
* Specify the DRM device name handling the HDMI output and the HDMI connector
|
||||
* corresponding to each CEC port. The order of connectors must match the order
|
||||
* in the EC (first connector is EC port 0, ...), and the number of connectors
|
||||
* must match the number of ports in the EC (which can be queried using the
|
||||
* EC_CMD_CEC_PORT_COUNT host command).
|
||||
*/
|
||||
|
||||
struct cec_dmi_match {
|
||||
const char *sys_vendor;
|
||||
const char *product_name;
|
||||
const char *devname;
|
||||
const char *const *conns;
|
||||
};
|
||||
|
||||
static const char *const port_b_conns[] = { "Port B", NULL };
|
||||
static const char *const port_c_conns[] = { "Port C", NULL };
|
||||
static const char *const port_db_conns[] = { "Port D", "Port B", NULL };
|
||||
static const char *const port_ba_conns[] = { "Port B", "Port A", NULL };
|
||||
static const char *const port_ab_conns[] = { "Port A", "Port B", NULL };
|
||||
static const char *const port_d_conns[] = { "Port D", NULL };
|
||||
|
||||
static const struct cec_dmi_match cec_dmi_match_table[] = {
|
||||
/* AMD Lilac */
|
||||
{ "AMD", "Lilac", "0000:03:00.0", port_c_conns },
|
||||
/* Valve Fremont */
|
||||
{ "OEM", "F7F", "0000:03:00.0", port_c_conns },
|
||||
{ "Valve", "Fremont", "0000:03:00.0", port_c_conns },
|
||||
/* Google Fizz */
|
||||
{ "Google", "Fizz", "0000:00:02.0", port_b_conns },
|
||||
/* Google Brask */
|
||||
{ "Google", "Brask", "0000:00:02.0", port_b_conns },
|
||||
/* Google Moli */
|
||||
{ "Google", "Moli", "0000:00:02.0", port_b_conns },
|
||||
/* Google Kinox */
|
||||
{ "Google", "Kinox", "0000:00:02.0", port_b_conns },
|
||||
/* Google Kuldax */
|
||||
{ "Google", "Kuldax", "0000:00:02.0", port_b_conns },
|
||||
/* Google Aurash */
|
||||
{ "Google", "Aurash", "0000:00:02.0", port_b_conns },
|
||||
/* Google Gladios */
|
||||
{ "Google", "Gladios", "0000:00:02.0", port_b_conns },
|
||||
/* Google Lisbon */
|
||||
{ "Google", "Lisbon", "0000:00:02.0", port_b_conns },
|
||||
/* Google Dibbi */
|
||||
{ "Google", "Dibbi", "0000:00:02.0", port_db_conns },
|
||||
/* Google Constitution */
|
||||
{ "Google", "Constitution", "0000:00:02.0", port_ba_conns },
|
||||
/* Google Boxy */
|
||||
{ "Google", "Boxy", "0000:00:02.0", port_d_conns },
|
||||
/* Google Taranza */
|
||||
{ "Google", "Taranza", "0000:00:02.0", port_db_conns },
|
||||
/* Google Dexi */
|
||||
{ "Google", "Dexi", "0000:00:02.0", port_db_conns },
|
||||
/* Google Dita */
|
||||
{ "Google", "Dita", "0000:00:02.0", port_db_conns },
|
||||
/* Google Dirks */
|
||||
{ "Google", "Dirks", "0000:00:02.0", port_ab_conns },
|
||||
/* Google Moxie */
|
||||
{ "Google", "Moxie", "0000:00:02.0", port_b_conns },
|
||||
/* AMD Lilac */
|
||||
{ "AMD", "Lilac", "0000:06:00.0", port_c_conns },
|
||||
};
|
||||
|
||||
static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
|
||||
const char * const **conns)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0 ; i < ARRAY_SIZE(cec_dmi_match_table) ; ++i) {
|
||||
const struct cec_dmi_match *m = &cec_dmi_match_table[i];
|
||||
|
||||
if (dmi_match(DMI_SYS_VENDOR, m->sys_vendor) &&
|
||||
dmi_match(DMI_PRODUCT_NAME, m->product_name)) {
|
||||
struct device *d;
|
||||
|
||||
/* Find the device, bail out if not yet registered */
|
||||
d = bus_find_device_by_name(&pci_bus_type, NULL,
|
||||
m->devname);
|
||||
if (!d)
|
||||
return ERR_PTR(-EPROBE_DEFER);
|
||||
put_device(d);
|
||||
*conns = m->conns;
|
||||
return d;
|
||||
}
|
||||
}
|
||||
|
||||
/* Hardware support must be added in the cec_dmi_match_table */
|
||||
dev_warn(dev, "CEC notifier not configured for this hardware\n");
|
||||
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static struct device *cros_ec_cec_find_hdmi_dev(struct device *dev,
|
||||
const char * const **conns)
|
||||
{
|
||||
return ERR_PTR(-ENODEV);
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
static int cros_ec_cec_get_num_ports(struct cros_ec_cec *cros_ec_cec)
|
||||
{
|
||||
struct ec_response_cec_port_count response;
|
||||
int ret;
|
||||
|
||||
ret = cros_ec_cmd(cros_ec_cec->cros_ec, 0, EC_CMD_CEC_PORT_COUNT, NULL,
|
||||
0, &response, sizeof(response));
|
||||
if (ret < 0) {
|
||||
/*
|
||||
* Old EC firmware only supports one port and does not support
|
||||
* the port count command, so fall back to assuming one port.
|
||||
*/
|
||||
cros_ec_cec->num_ports = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (response.port_count == 0) {
|
||||
dev_err(cros_ec_cec->cros_ec->dev,
|
||||
"EC reports 0 CEC ports\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
if (response.port_count > EC_CEC_MAX_PORTS) {
|
||||
dev_err(cros_ec_cec->cros_ec->dev,
|
||||
"EC reports too many ports: %d\n", response.port_count);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
cros_ec_cec->num_ports = response.port_count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_cec_get_write_cmd_version(struct cros_ec_cec *cros_ec_cec)
|
||||
{
|
||||
struct cros_ec_device *cros_ec = cros_ec_cec->cros_ec;
|
||||
struct ec_params_get_cmd_versions_v1 params = {
|
||||
.cmd = EC_CMD_CEC_WRITE_MSG,
|
||||
};
|
||||
struct ec_response_get_cmd_versions response;
|
||||
int ret;
|
||||
|
||||
ret = cros_ec_cmd(cros_ec, 1, EC_CMD_GET_CMD_VERSIONS, ¶ms,
|
||||
sizeof(params), &response, sizeof(response));
|
||||
if (ret < 0) {
|
||||
dev_err(cros_ec->dev,
|
||||
"error getting CEC write command version: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (response.version_mask & EC_VER_MASK(1)) {
|
||||
cros_ec_cec->write_cmd_version = 1;
|
||||
} else {
|
||||
if (cros_ec_cec->num_ports != 1) {
|
||||
dev_err(cros_ec->dev,
|
||||
"v0 write command only supports 1 port, %d reported\n",
|
||||
cros_ec_cec->num_ports);
|
||||
return -EINVAL;
|
||||
}
|
||||
cros_ec_cec->write_cmd_version = 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int cros_ec_cec_init_port(struct device *dev,
|
||||
struct cros_ec_cec *cros_ec_cec,
|
||||
int port_num, struct device *hdmi_dev,
|
||||
const char * const *conns)
|
||||
{
|
||||
struct cros_ec_cec_port *port;
|
||||
int ret;
|
||||
|
||||
port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL);
|
||||
if (!port)
|
||||
return -ENOMEM;
|
||||
|
||||
port->cros_ec_cec = cros_ec_cec;
|
||||
port->port_num = port_num;
|
||||
|
||||
port->adap = cec_allocate_adapter(&cros_ec_cec_ops, port, DRV_NAME,
|
||||
CEC_CAP_DEFAULTS |
|
||||
CEC_CAP_CONNECTOR_INFO, 1);
|
||||
if (IS_ERR(port->adap))
|
||||
return PTR_ERR(port->adap);
|
||||
|
||||
if (!conns[port_num]) {
|
||||
dev_err(dev, "no conn for port %d\n", port_num);
|
||||
ret = -ENODEV;
|
||||
goto out_probe_adapter;
|
||||
}
|
||||
|
||||
port->notify = cec_notifier_cec_adap_register(hdmi_dev, conns[port_num],
|
||||
port->adap);
|
||||
if (!port->notify) {
|
||||
ret = -ENOMEM;
|
||||
goto out_probe_adapter;
|
||||
}
|
||||
|
||||
ret = cec_register_adapter(port->adap, dev);
|
||||
if (ret < 0)
|
||||
goto out_probe_notify;
|
||||
|
||||
cros_ec_cec->ports[port_num] = port;
|
||||
|
||||
return 0;
|
||||
|
||||
out_probe_notify:
|
||||
cec_notifier_cec_adap_unregister(port->notify, port->adap);
|
||||
out_probe_adapter:
|
||||
cec_delete_adapter(port->adap);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int cros_ec_cec_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_dev *ec_dev = dev_get_drvdata(pdev->dev.parent);
|
||||
struct cros_ec_device *cros_ec = ec_dev->ec_dev;
|
||||
struct cros_ec_cec *cros_ec_cec;
|
||||
struct cros_ec_cec_port *port;
|
||||
struct device *hdmi_dev;
|
||||
const char * const *conns = NULL;
|
||||
int ret;
|
||||
|
||||
hdmi_dev = cros_ec_cec_find_hdmi_dev(&pdev->dev, &conns);
|
||||
if (IS_ERR(hdmi_dev))
|
||||
return PTR_ERR(hdmi_dev);
|
||||
|
||||
cros_ec_cec = devm_kzalloc(&pdev->dev, sizeof(*cros_ec_cec),
|
||||
GFP_KERNEL);
|
||||
if (!cros_ec_cec)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, cros_ec_cec);
|
||||
cros_ec_cec->cros_ec = cros_ec;
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
ret = cros_ec_cec_get_num_ports(cros_ec_cec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = cros_ec_cec_get_write_cmd_version(cros_ec_cec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
for (int i = 0; i < cros_ec_cec->num_ports; i++) {
|
||||
ret = cros_ec_cec_init_port(&pdev->dev, cros_ec_cec, i,
|
||||
hdmi_dev, conns);
|
||||
if (ret)
|
||||
goto unregister_ports;
|
||||
}
|
||||
|
||||
/* Get CEC events from the EC. */
|
||||
cros_ec_cec->notifier.notifier_call = cros_ec_cec_event;
|
||||
ret = blocking_notifier_chain_register(&cros_ec->event_notifier,
|
||||
&cros_ec_cec->notifier);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to register notifier\n");
|
||||
goto unregister_ports;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
unregister_ports:
|
||||
/*
|
||||
* Unregister any adapters which have been registered. We don't add the
|
||||
* port to the array until the adapter has been registered successfully,
|
||||
* so any non-NULL ports must have been registered.
|
||||
*/
|
||||
for (int i = 0; i < cros_ec_cec->num_ports; i++) {
|
||||
port = cros_ec_cec->ports[i];
|
||||
if (!port)
|
||||
break;
|
||||
cec_notifier_cec_adap_unregister(port->notify, port->adap);
|
||||
cec_unregister_adapter(port->adap);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void cros_ec_cec_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct cros_ec_cec *cros_ec_cec = platform_get_drvdata(pdev);
|
||||
struct device *dev = &pdev->dev;
|
||||
struct cros_ec_cec_port *port;
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* blocking_notifier_chain_unregister() only fails if the notifier isn't
|
||||
* in the list. We know it was added to it by .probe(), so there should
|
||||
* be no need for error checking. Be cautious and still check.
|
||||
*/
|
||||
ret = blocking_notifier_chain_unregister(
|
||||
&cros_ec_cec->cros_ec->event_notifier,
|
||||
&cros_ec_cec->notifier);
|
||||
if (ret)
|
||||
dev_err(dev, "failed to unregister notifier\n");
|
||||
|
||||
for (int i = 0; i < cros_ec_cec->num_ports; i++) {
|
||||
port = cros_ec_cec->ports[i];
|
||||
cec_notifier_cec_adap_unregister(port->notify, port->adap);
|
||||
cec_unregister_adapter(port->adap);
|
||||
}
|
||||
}
|
||||
|
||||
static const struct platform_device_id cros_ec_cec_id[] = {
|
||||
{ DRV_NAME, 0 },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(platform, cros_ec_cec_id);
|
||||
|
||||
static struct platform_driver cros_ec_cec_driver = {
|
||||
.probe = cros_ec_cec_probe,
|
||||
.remove = cros_ec_cec_remove,
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
.pm = &cros_ec_cec_pm_ops,
|
||||
},
|
||||
.id_table = cros_ec_cec_id,
|
||||
};
|
||||
|
||||
module_platform_driver(cros_ec_cec_driver);
|
||||
|
||||
MODULE_DESCRIPTION("CEC driver for ChromeOS ECs");
|
||||
MODULE_AUTHOR("Neil Armstrong <narmstrong@baylibre.com>");
|
||||
MODULE_LICENSE("GPL");
|
||||
@@ -0,0 +1,51 @@
|
||||
#!/bin/bash
|
||||
# Steamify: gamescope-session starts gamescope without a size or a refresh rate, so it takes the display's *preferred* mode,
|
||||
# which many monitors declare as 60 Hz (the faster modes sit in an extension block of the EDID). This starts it the way the
|
||||
# desktop runs that display: the mode KDE has saved for the connected display (System Settings > Display), when the display
|
||||
# still has it, else its biggest mode at the highest refresh rate.
|
||||
# Read at every session start (modetest, part of libdrm which gamescope needs, and KDE's ~/.config/kwinoutputconfig.json), so
|
||||
# changing the resolution in KDE, another monitor or a TV just works. Anything unclear (no modetest output, no modes, more than
|
||||
# one display connected: gamescope chooses which one it uses) leaves gamescope's own choice alone.
|
||||
real=/usr/bin/gamescope
|
||||
|
||||
modes() {
|
||||
# One line per mode of every connected connector: "<connector> <width> <height> <refresh>"
|
||||
local m
|
||||
for m in nvidia-drm amdgpu i915 xe nouveau radeon; do
|
||||
timeout 10 modetest -M "$m" -c 2>/dev/null | awk '
|
||||
/^[0-9]+[ \t]+[0-9]+[ \t]+(connected|disconnected|unknown)/ { conn = ($3 == "connected") ? $4 : ""; next }
|
||||
conn != "" && /^[ \t]+#[0-9]+ [0-9]+x[0-9]+ [0-9.]+ / && !/interlace/ {
|
||||
split($2, wh, "x"); printf "%s %d %d %.0f\n", conn, wh[1], wh[2], $3
|
||||
}'
|
||||
done
|
||||
}
|
||||
|
||||
kde_mode() {
|
||||
# kde_mode <connector>: "<width> <height> <refresh>" as KDE has it saved for that connector, or nothing.
|
||||
python3 -I - "$1" "${XDG_CONFIG_HOME:-$HOME/.config}/kwinoutputconfig.json" 2>/dev/null << 'EOF'
|
||||
import json, sys
|
||||
try:
|
||||
for block in json.load(open(sys.argv[2])):
|
||||
if block.get("name") == "outputs":
|
||||
for o in block.get("data", []):
|
||||
m = o.get("mode")
|
||||
if o.get("connectorName") == sys.argv[1] and m:
|
||||
print(m["width"], m["height"], round(m["refreshRate"] / 1000))
|
||||
except Exception:
|
||||
pass
|
||||
EOF
|
||||
}
|
||||
|
||||
args=()
|
||||
all="$(modes)"
|
||||
conn="$(awk '{print $1}' <<< "$all" | sort -u)"
|
||||
if [[ -n "$conn" && "$(wc -l <<< "$conn")" == 1 ]]; then
|
||||
read -r w h r <<< "$(kde_mode "$conn")"
|
||||
# KDE's mode only counts when the display really has it (a refresh rate within 1 Hz).
|
||||
if [[ -z "${w:-}" ]] || ! awk -v w="$w" -v h="${h:-0}" -v r="${r:-0}" '$2 == w && $3 == h && ($4 - r < 2 && r - $4 < 2) {f = 1} END {exit !f}' <<< "$all"; then
|
||||
read -r _ w h r < <(awk '{print $2 * $3, $2, $3, $4}' <<< "$all" | sort -k1,1nr -k4,4nr | head -1 | awk '{print "x", $2, $3, $4}')
|
||||
fi
|
||||
[[ "${w:-0}" -gt 0 && "${h:-0}" -gt 0 && "${r:-0}" -gt 0 ]] && args=(-W "$w" -H "$h" -r "$r")
|
||||
fi
|
||||
if [[ "${1:-}" == --print-args ]]; then echo "${args[*]}"; exit 0; fi
|
||||
exec "$real" "${args[@]}" "$@"
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,128 @@
|
||||
# Maintainer: theupriser
|
||||
# Based on Arch Linux's gamescope PKGBUILD (Maxime Gauduin, Giancarlo Razzolini, Sven-Hendrik Haase and contributors).
|
||||
#
|
||||
# gamescope with GBM-allocated scan-out buffers for NVIDIA (opt-in: gamescope_drm_gbm_scanout=1).
|
||||
# The patches are gamescope-gbm-scanout.patch and gamescope-ui-native.patch (next to this file, applied in this order); see the
|
||||
# README for where they come from.
|
||||
# UPSTREAM_VERSION (next to this file) names the gamescope tag the patches are made for.
|
||||
|
||||
pkgname=steamify-gamescope-nvidia
|
||||
_srcname=gamescope
|
||||
pkgver=3.16.31
|
||||
pkgrel=3
|
||||
pkgdesc='SteamOS session compositing window manager, with GBM scan-out for NVIDIA (opt-in: gamescope_drm_gbm_scanout=1)'
|
||||
arch=(x86_64)
|
||||
url=https://github.com/theupriser/steamify-gamescope-nvidia
|
||||
license=(
|
||||
BSD-2-Clause
|
||||
BSD-3-Clause
|
||||
LicenseRef-Reshade
|
||||
)
|
||||
provides=("gamescope=$pkgver")
|
||||
conflicts=(gamescope gamescope-nvidia-gbm)
|
||||
replaces=(gamescope-nvidia-gbm)
|
||||
depends=(
|
||||
glibc
|
||||
mesa
|
||||
libstdc++
|
||||
libgcc
|
||||
lcms2
|
||||
libavif
|
||||
libcap.so
|
||||
libdecor
|
||||
libdrm
|
||||
libei
|
||||
libinput
|
||||
libpipewire-0.3.so
|
||||
libx11
|
||||
libxcb
|
||||
libxcomposite
|
||||
libxcursor
|
||||
libxdamage
|
||||
libxext
|
||||
libxfixes
|
||||
libxi
|
||||
libxkbcommon.so
|
||||
libxmu
|
||||
libxrender
|
||||
libxres
|
||||
libxtst
|
||||
libxxf86vm
|
||||
luajit
|
||||
pixman
|
||||
sdl2
|
||||
seatd
|
||||
systemd-libs
|
||||
vulkan-icd-loader
|
||||
wayland
|
||||
xcb-util-errors
|
||||
xcb-util-wm
|
||||
xorg-server-xwayland
|
||||
)
|
||||
makedepends=(
|
||||
benchmark
|
||||
cmake
|
||||
git
|
||||
glslang
|
||||
meson
|
||||
ninja
|
||||
vulkan-headers
|
||||
wayland-protocols
|
||||
catch2
|
||||
)
|
||||
source=(
|
||||
gamescope::git+https://github.com/ValveSoftware/gamescope.git#commit=${pkgver}
|
||||
gamescope-gbm-scanout.patch
|
||||
gamescope-ui-native.patch
|
||||
git+https://gitlab.freedesktop.org/emersion/libdisplay-info.git
|
||||
git+https://gitlab.freedesktop.org/emersion/libliftoff.git
|
||||
git+https://github.com/ValveSoftware/openvr.git
|
||||
git+https://github.com/Joshua-Ashton/reshade.git
|
||||
git+https://github.com/KhronosGroup/SPIRV-Headers.git
|
||||
git+https://github.com/Joshua-Ashton/vkroots.git
|
||||
git+https://gitlab.freedesktop.org/wlroots/wlroots.git
|
||||
)
|
||||
b2sums=('SKIP'
|
||||
'SKIP'
|
||||
'SKIP'
|
||||
'SKIP'
|
||||
'SKIP'
|
||||
'SKIP'
|
||||
'SKIP'
|
||||
'SKIP'
|
||||
'SKIP'
|
||||
'SKIP')
|
||||
|
||||
prepare() {
|
||||
cd gamescope
|
||||
|
||||
# GBM scan-out for NVIDIA. If it no longer applies to this gamescope version, stop here: the build must not
|
||||
# silently produce an unpatched gamescope under this package's name.
|
||||
git apply --check ../gamescope-gbm-scanout.patch || { echo "gbm patch does not apply to gamescope ${pkgver}" >&2; return 1; }
|
||||
git apply ../gamescope-gbm-scanout.patch
|
||||
git apply --check ../gamescope-ui-native.patch || { echo "ui-native patch does not apply to gamescope ${pkgver}" >&2; return 1; }
|
||||
git apply ../gamescope-ui-native.patch
|
||||
|
||||
meson subprojects download
|
||||
|
||||
for submodule in src/reshade subprojects/{libdisplay-info,libliftoff,openvr,vkroots,wlroots} thirdparty/SPIRV-Headers; do
|
||||
git submodule init ${submodule}
|
||||
git config submodule.${submodule}.url ../${submodule##*/}
|
||||
done
|
||||
git -c protocol.file.allow=always submodule update
|
||||
}
|
||||
|
||||
build() {
|
||||
arch-meson gamescope build \
|
||||
-Dforce_fallback_for=glm,stb,libdisplay-info,libliftoff,vkroots \
|
||||
-Dpipewire=enabled
|
||||
meson compile -C build
|
||||
}
|
||||
|
||||
package() {
|
||||
DESTDIR="${pkgdir}" meson install -C build \
|
||||
--skip-subprojects
|
||||
install -Dm 644 gamescope/LICENSE -t "${pkgdir}"/usr/share/licenses/${pkgname}/
|
||||
}
|
||||
|
||||
# vim: ts=2 sw=2 et:
|
||||
@@ -0,0 +1,39 @@
|
||||
Keeps Steam's UI X server (server 0) at the output size when Steam asks for a smaller one.
|
||||
|
||||
With -steamos3 (what gamescope-session-cachyos passes) Steam sets its UI to 1920x1080 right after it starts
|
||||
(GAMESCOPE_XWAYLAND_MODE_CONTROL), so on a bigger display, and on one that isn't 16:9, the whole interface is a scaled-up
|
||||
1080p picture. Games already run at the display's size. With this the server stays at the output size and Steam draws its
|
||||
window to match; Steam's SteamOS features (settings pages, game launching) keep working. Written for Steamify on an NVIDIA
|
||||
PC (RTX 5080, 3440x1440@175, tested with Steam's UI and a Proton game); not part of the farnoy patch. On by default; the
|
||||
setting gamescope_xwayland_ui_native=0 (in gamescope's environment) turns it off.
|
||||
Applies with `git apply` after gamescope-gbm-scanout.patch (it ignores these lines).
|
||||
|
||||
diff --git a/src/steamcompmgr.cpp b/src/steamcompmgr.cpp
|
||||
index 2cb5eb5..ce14c8d 100644
|
||||
--- a/src/steamcompmgr.cpp
|
||||
+++ b/src/steamcompmgr.cpp
|
||||
@@ -129,6 +129,8 @@ LogScope g_WaitableLog("waitable");
|
||||
|
||||
gamescope::ConVar<bool> cv_overlay_unmultiplied_alpha{ "overlay_unmultiplied_alpha", false };
|
||||
|
||||
+gamescope::ConVar<bool> cv_xwayland_ui_native( "xwayland_ui_native", true, "Keep the Steam UI X server (server 0) at the output size when Steam asks for a smaller one." );
|
||||
+
|
||||
gamescope::ConVar<bool> cv_vr_show_forwarded_overlays{ "vr_show_forwarded_overlays", false };
|
||||
|
||||
std::string *g_pVROverlayKey = nullptr;
|
||||
@@ -7240,6 +7242,15 @@ handle_property_notify(xwayland_ctx_t *ctx, XPropertyEvent *ev)
|
||||
height = std::min<int>(height, currentOutputHeight);
|
||||
}
|
||||
|
||||
+ // Steam asks for a smaller size for its own UI (server 0, 1920x1080 with -steamos3), which makes the whole
|
||||
+ // interface a scaled-up picture on a bigger display, and on one that isn't 16:9 as well. The UI server
|
||||
+ // stays at the output size: Steam sizes its window to the server.
|
||||
+ if ( server_idx == 0 && cv_xwayland_ui_native )
|
||||
+ {
|
||||
+ width = currentOutputWidth;
|
||||
+ height = currentOutputHeight;
|
||||
+ }
|
||||
+
|
||||
gamescope_xwayland_server_t *server = wlserver_get_xwayland_server( server_idx );
|
||||
if ( server )
|
||||
{
|
||||
@@ -0,0 +1,17 @@
|
||||
# Maintainer: theupriser
|
||||
# An empty package that only tells pacman "python-hid is there". For a PC that keeps python-hidapi (liquidctl needs it):
|
||||
# the real python-hid conflicts with it, and jupiter-hw-support (a dependency of gamescope-session-cachyos) wants python-hid.
|
||||
# Only Valve's Steam Deck controller firmware updater in jupiter-hw-support uses it, which a PC doesn't run.
|
||||
# Steamify (lib/packages.sh) builds it with makepkg and installs it before the conversion's packages.
|
||||
pkgname=steamify-python-hid-shim
|
||||
pkgver=1
|
||||
pkgrel=1
|
||||
pkgdesc='Placeholder: python-hid for PCs that keep python-hidapi (they conflict)'
|
||||
arch=(any)
|
||||
license=(MIT)
|
||||
depends=(python-hidapi)
|
||||
provides=('python-hid=99')
|
||||
|
||||
package() {
|
||||
:
|
||||
}
|
||||
@@ -7,6 +7,11 @@
|
||||
* leaves set on GPIO pin 18 (_SB.PCI0.GPP6) stays set, and the machine starts again right after powering off.
|
||||
* Valve's kernel clears that bit at probe on Fremont (not for upstream);
|
||||
* this does the same right before power-off, for kernels without it.
|
||||
*
|
||||
* What it does follows Valve's Steam Machine kernel
|
||||
* (https://github.com/evlaV/linux-integration, GPL-2.0 like the rest of the
|
||||
* Linux kernel); this module is Steamify's own implementation of it, also
|
||||
* GPL-2.0. Not affiliated with or endorsed by Valve Corporation.
|
||||
*/
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
|
||||
+2
-2
@@ -15,11 +15,11 @@
|
||||
set -uo pipefail
|
||||
|
||||
# Release version, see CHANGELOG.md.
|
||||
VERSION=2.11.0
|
||||
VERSION=2.12.0
|
||||
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
|
||||
for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh 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 gamescope-nvidia controllers cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier first-login menu backend; do
|
||||
# shellcheck source=/dev/null
|
||||
source "$SCRIPT_DIR/lib/$lib.sh"
|
||||
done
|
||||
|
||||
+4
-2
@@ -65,7 +65,8 @@ Item {
|
||||
var w = {};
|
||||
for (var i = 0; i < items.length; i++) w[items[i].id] = items[i].kind === "action" ? false : items[i].wanted;
|
||||
want = w;
|
||||
boot = nowOn("boot") ? "desktop" : "gamescope";
|
||||
// (What the PC has, or what the backend ticked: "Gaming on NVIDIA" moving to the conversion keeps how Steam started.)
|
||||
boot = want.boot ? "desktop" : "gamescope";
|
||||
if (sel > rows.length) sel = 0;
|
||||
}
|
||||
Connections { target: backend; function onStatusChanged() { if (app.screen === "menu" || app.screen === "done") app.syncFromStatus(); } }
|
||||
@@ -101,7 +102,8 @@ Item {
|
||||
if (it.kind !== "toggle") continue;
|
||||
if (hidden(it)) { if (it.on) p.push({ id: it.id, action: "off" }); continue; }
|
||||
if (it.parent && !want[it.parent]) { if (it.on) p.push({ id: it.id, action: "off" }); continue; }
|
||||
if (want[it.id] && !it.on) p.push({ id: it.id, action: "on", isNew: !!it["new"] });
|
||||
// (An item that is off but flagged "update" replaces what the PC has: the conversion for "Gaming on NVIDIA".)
|
||||
if (want[it.id] && !it.on) p.push({ id: it.id, action: it.update ? "update" : "on", isNew: !!it["new"] });
|
||||
else if (!want[it.id] && it.on) p.push({ id: it.id, action: "off" });
|
||||
else if (want[it.id] && (reapply || it.update)) p.push({ id: it.id, action: reapply ? "again" : "update" });
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user