diff --git a/AGENTS.md b/AGENTS.md index 54da57b..65a1e46 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -127,6 +127,11 @@ gamescope and the Plasma desktop. Primary target: the Valve Steam Machine it. Only the release branch's pull request into `main` is the user's: never commit or merge to `main`, the user merges that one. + A `.no-release-yet` file at the root of a feature/bugfix branch means + it must not be merged into the release branch yet (work in progress, + untested on hardware). When the feature is complete, ask the user + whether it may be released; on a yes, delete the file, then open the + pull request and merge it. Keep a feature/bugfix branch up to date by merging (or rebasing on) its release branch, the release branch by merging `main` when that moved. diff --git a/CHANGELOG.md b/CHANGELOG.md index c3e6465..3a9858e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,8 @@ one per merged pull request. ## 2.9.7 - 2026-10-01 +- **feat: the SteamOS conversion fixes a corrupted gaming mode screen with an NVIDIA GPU: nvidia-drm.modeset=1 and fbdev=1 on the kernel command line, NVIDIA modules in the initramfs (lib/nvidia.sh)** + ## 2.9.6 - 2026-09-30 - **fix(ci): a dev ISO from a release branch is built with that branch's own Steamify (input steamify_ref of the ISO workflow), not the newest published release: release/2.9.6 kept producing 2.9.5 ISOs** diff --git a/TECHNICAL.md b/TECHNICAL.md index 20da47e..c04f9ce 100644 --- a/TECHNICAL.md +++ b/TECHNICAL.md @@ -206,6 +206,19 @@ or upgraded. A newly added kernel doesn't come with its headers, so `ensure-kernel-headers.service` checks at every boot and installs any missing `-headers` package, which makes DKMS build the driver for it. +## NVIDIA fix for gaming mode + +gamescope drives the display itself through DRM/KMS, which with NVIDIA needs +kernel modesetting and the framebuffer driver, and the driver loaded early; +without them gaming mode can show a corrupted image. With an NVIDIA GPU and +its driver installed (`lib/nvidia.sh`), the SteamOS conversion adds +`nvidia-drm.modeset=1 nvidia-drm.fbdev=1` to the kernel command line (Limine, +systemd-boot or GRUB; the file is backed up first) and +`/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf` (`MODULES+=(nvidia +nvidia_modeset nvidia_uvm nvidia_drm)`), then rebuilds the initramfs and boot +entries. Only what's missing is changed; it applies after a reboot. Turning +the conversion off removes both. Untested on hardware so far. + ## HDMI-CEC The **HDMI-CEC** item (on every PC; ticked by default only on a Steam @@ -510,6 +523,7 @@ immediately, which can turn into a loop - see | `lib/steam-game.sh` | Add as non-Steam game: Steamify in the Steam library (`patches/steam-shortcuts.py`) | | `lib/update-notifier.sh` | Update notifications: the notifier from `patches/` and its user timer | | `lib/first-login.sh` | `--defaults` without a session (the Steam Machine ISO's installer): the one-time first-login step that sets up single user's launcher on Plasma's new layout and opens the app | +| `lib/nvidia.sh` | NVIDIA fix for gaming mode: kernel parameters and early modules (part of the SteamOS conversion) | | `lib/vram-booster.sh` | VRAM booster (`dmemcg-booster`, `plasma-foreground-booster`) | | `services/` | The systemd units the scripts install (`service_file`, `@KEY@` placeholders); see its README | | `patches/` | Module sources and patches the scripts build or apply (`patch_file`); see its README | diff --git a/lib/login-manager.sh b/lib/login-manager.sh index bd35013..40d0280 100644 --- a/lib/login-manager.sh +++ b/lib/login-manager.sh @@ -109,6 +109,7 @@ gaming_enable() { esac create_desktop_shortcut steam_enable + nvidia_enable || return 1 os_name_enable || return 1 } @@ -124,6 +125,7 @@ gaming_disable() { steam_disable krevert gaming os_name_disable + nvidia_disable ok "The PC boots to the normal login screen again (from the next boot)." } diff --git a/lib/menu.sh b/lib/menu.sh index 4356cee..2c99303 100644 --- a/lib/menu.sh +++ b/lib/menu.sh @@ -25,7 +25,7 @@ NO_PRESELECT=(boot cec kpin hdmi) # FEATURE_BASELINE. FEATURE_BASELINE=2.1.0 declare -A FEATURE_VERSION=( - [gaming]=2.9.0 [boot]=2.1.0 [theme]=2.1.0 [glyphs]=2.1.0 [single]=2.1.0 + [gaming]=2.9.7 [boot]=2.1.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 [kpin]=2.1.0 [hdmi]=2.1.0 ) diff --git a/lib/nvidia.sh b/lib/nvidia.sh new file mode 100644 index 0000000..ef08329 --- /dev/null +++ b/lib/nvidia.sh @@ -0,0 +1,102 @@ +#!/bin/bash +# NVIDIA fix for gaming mode, part of the SteamOS conversion +# (gaming_enable/gaming_disable). gamescope drives the display itself +# through DRM/KMS; with NVIDIA that needs kernel modesetting and the +# framebuffer driver (nvidia-drm.modeset=1 nvidia-drm.fbdev=1) and the +# driver in the initramfs, or the screen shows a corrupted image. +# Does nothing without an NVIDIA GPU that has its driver installed. +# Sourced by steamify.sh; not meant to be run on its own. + +NVIDIA_PARAMS="nvidia-drm.modeset=1 nvidia-drm.fbdev=1" +NVIDIA_INITRAMFS_CONF=/etc/mkinitcpio.conf.d/90-steamify-nvidia.conf +NVIDIA_MARK="# steamify-nvidia" + +nvidia_present() { + # An NVIDIA GPU (PCI vendor 0x10de, display class) and its kernel driver. + local d + for d in /sys/class/drm/card[0-9]*/device; do + [[ "$(cat "$d/vendor" 2>/dev/null)" == 0x10de ]] || continue + [[ "$(cat "$d/class" 2>/dev/null)" == 0x03* ]] && modinfo nvidia_drm >/dev/null 2>&1 && return 0 + done + return 1 +} + +nvidia_boot_file() { + # Same boot loaders as the kernel command line elsewhere (lib/hdmi-refresh.sh). + hdmi_boot_file +} + +nvidia_param_set() { + # 0 when $1 (e.g. nvidia-drm.modeset=1) is on the kernel command line or + # already in the boot loader's file (not booted yet). + local f; f="$(nvidia_boot_file)" + grep -qw -- "$1" /proc/cmdline 2>/dev/null || + { [[ -n "$f" ]] && grep -qw -- "$1" "$f" 2>/dev/null; } +} + +nvidia_params_missing() { + local p + for p in $NVIDIA_PARAMS; do nvidia_param_set "$p" || return 0; done + return 1 +} + +nvidia_initramfs_ok() { + [[ -f "$NVIDIA_INITRAMFS_CONF" ]] || grep -qE '^MODULES=.*nvidia_drm' /etc/mkinitcpio.conf 2>/dev/null +} + +nvidia_rebuild_boot() { + local f; f="$(nvidia_boot_file)" + info "Rebuilding the initramfs and boot entries..." + case "$f" in + /etc/default/limine) sudo limine-mkinitcpio ;; + /etc/sdboot-manage.conf) sudo /usr/bin/mkinitcpio -P && sudo sdboot-manage gen ;; + /etc/default/grub) sudo /usr/bin/mkinitcpio -P && sudo grub-mkconfig -o /boot/grub/grub.cfg ;; + *) sudo /usr/bin/mkinitcpio -P ;; + esac +} + +nvidia_enable() { + nvidia_present || return 0 + local f changed=0 p; f="$(nvidia_boot_file)" + if nvidia_params_missing; then + if [[ -z "$f" ]]; then + warn "NVIDIA GPU found, but no supported boot loader: add '$NVIDIA_PARAMS' to the kernel command line yourself." + else + info "NVIDIA GPU found: adding $NVIDIA_PARAMS to the kernel command line (fixes a corrupted screen in gaming mode)..." + backup_file "$f" + case "$f" in + /etc/default/limine) + printf 'KERNEL_CMDLINE[default]+=" %s" %s\n' "$NVIDIA_PARAMS" "$NVIDIA_MARK" | sudo tee -a "$f" >/dev/null ;; + /etc/sdboot-manage.conf) + sudo sed -i -E "s/^(LINUX_OPTIONS=\"[^\"]*)\"/\1 $NVIDIA_PARAMS\"/" "$f" ;; + /etc/default/grub) + sudo sed -i -E "s/^(GRUB_CMDLINE_LINUX_DEFAULT=\"[^\"]*)\"/\1 $NVIDIA_PARAMS\"/" "$f" ;; + esac + changed=1 + fi + fi + if ! nvidia_initramfs_ok; then + info "Loading the NVIDIA modules early (initramfs)..." + printf 'MODULES+=(nvidia nvidia_modeset nvidia_uvm nvidia_drm)\n' | + sudo install -Dm644 /dev/stdin "$NVIDIA_INITRAMFS_CONF" && changed=1 + fi + if [[ $changed == 1 ]]; then + nvidia_rebuild_boot || { err "Rebuilding the boot entries failed."; return 1; } + ok "NVIDIA gaming mode fix applied; it takes effect after a reboot." + fi + return 0 +} + +nvidia_disable() { + local f changed=0; f="$(nvidia_boot_file)" + if [[ -n "$f" ]] && grep -q "$NVIDIA_PARAMS" "$f" 2>/dev/null; then + case "$f" in + /etc/default/limine) sudo sed -i "/$NVIDIA_MARK\$/d" "$f" ;; + *) sudo sed -i "s/ \?$NVIDIA_PARAMS//" "$f" ;; + esac + changed=1 + fi + [[ -f "$NVIDIA_INITRAMFS_CONF" ]] && { sudo rm -f "$NVIDIA_INITRAMFS_CONF"; changed=1; } + [[ $changed == 1 ]] && nvidia_rebuild_boot + return 0 +} diff --git a/steamify.sh b/steamify.sh index 0943d72..1050c83 100755 --- a/steamify.sh +++ b/steamify.sh @@ -19,7 +19,7 @@ VERSION=2.9.7 SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" -for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh cec boot-session vapor-theme steamos-extras bios desktop-shortcut wizard-shortcut steam-game update-notifier first-login menu backend; do +for lib in common state packages login-manager single-user steam-desktop steam-machine fremont-poweroff vram-booster hdmi-refresh nvidia 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