From 21db1a8b1eb5b9c65fa8f4157b33be262a66ff27 Mon Sep 17 00:00:00 2001 From: Logan Gagne Date: Sat, 10 Oct 2026 09:53:38 -0400 Subject: [PATCH] fix(stl-game-config): detect primary-display HDR and the gaming GPU correctly check_kde_hdr_enabled always returned false (ANSI codes in kscreen-doctor -o defeated the grep, and head -1 read an arbitrary output). detect_gpu_vendor's sysfs checks never matched (ls instead of cat) and only looked at card0, which is often the CPU's iGPU. - Parse kscreen-doctor -j with jq; report hdr of the enabled priority-1 output - Rank every /sys/class/drm card: NVIDIA > discrete AMD > Intel > AMD iGPU (amdgpu VRAM < 2 GiB = iGPU); nvidia-smi -L and vulkaninfo fallbacks - Vulkan fallback grep is now -q so matched lines no longer leak into the JSON - Add tests/stl-game-config/test-system-info.sh (14 cases) - Bump stl-game-config to 1.2.2 Co-Authored-By: Claude Opus 5.5 --- .../.claude-plugin/plugin.json | 2 +- .../stl-game-config/scripts/system-info.sh | 139 +++++++++++------- .../.claude-plugin/plugin.json | 2 +- tests/stl-game-config/test-system-info.sh | 104 +++++++++++++ 4 files changed, 191 insertions(+), 56 deletions(-) create mode 100755 tests/stl-game-config/test-system-info.sh diff --git a/plugins-claude/stl-game-config/.claude-plugin/plugin.json b/plugins-claude/stl-game-config/.claude-plugin/plugin.json index 5750be7..03fbc4c 100644 --- a/plugins-claude/stl-game-config/.claude-plugin/plugin.json +++ b/plugins-claude/stl-game-config/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "stl-game-config", - "version": "1.2.1", + "version": "1.2.2", "description": "Inspect, lint, and configure Steam games via SteamTinkerLaunch — live system/config reporting with JSON output, config linting, and template-based generation", "author": { "name": "Logan Gagne" diff --git a/plugins-claude/stl-game-config/scripts/system-info.sh b/plugins-claude/stl-game-config/scripts/system-info.sh index ddcde6a..0e8e181 100755 --- a/plugins-claude/stl-game-config/scripts/system-info.sh +++ b/plugins-claude/stl-game-config/scripts/system-info.sh @@ -4,73 +4,104 @@ set -e -# Detect GPU vendor +# sysfs DRM root; overridable so tests can supply fixture cards +DRM_ROOT="${STL_DRM_ROOT:-/sys/class/drm}" + +# Detect the vendor of the GPU used for gaming. card0 is often the CPU's iGPU +# (e.g. Ryzen iGPU as card0, discrete NVIDIA as card1), so rank every card: +# NVIDIA > discrete AMD > Intel > AMD iGPU. An amdgpu device with under 2 GiB +# of VRAM is treated as an iGPU (its VRAM is a small BIOS carve-out). detect_gpu_vendor() { - if command -v nvidia-smi &>/dev/null; then - echo "nvidia" - elif command -v amdgpu-pro-info &>/dev/null || ls /sys/class/drm/card0/device/vendor 2>/dev/null | grep -q "1002"; then - echo "amd" - elif ls /sys/class/drm/card0/device/vendor 2>/dev/null | grep -q "8086"; then - echo "intel" - else - # Fallback: check Vulkan devices - if command -v vulkaninfo &>/dev/null; then - vulkaninfo --summary 2>/dev/null | grep -i "NVIDIA" && echo "nvidia" && return - vulkaninfo --summary 2>/dev/null | grep -i "AMD" && echo "amd" && return - vulkaninfo --summary 2>/dev/null | grep -i "Intel" && echo "intel" && return - fi - echo "unknown" + local card vendor vram score best_score=0 best="" + for card in "$DRM_ROOT"/card[0-9]*; do + # Skip connector entries such as card0-DP-1 + [[ "$(basename "$card")" =~ ^card[0-9]+$ ]] || continue + vendor=$(cat "$card/device/vendor" 2>/dev/null) || continue + case "$vendor" in + 0x10de) score=4 vendor=nvidia ;; + 0x1002) + vram=$(cat "$card/device/mem_info_vram_total" 2>/dev/null) || vram=0 + if [ "${vram:-0}" -ge 2147483648 ]; then score=3; else score=1; fi + vendor=amd + ;; + 0x8086) score=2 vendor=intel ;; + *) continue ;; + esac + if [ "$score" -gt "$best_score" ]; then + best_score=$score + best=$vendor fi + done + if [ -n "$best" ]; then + echo "$best" + return + fi + + # Fallback: NVIDIA proprietary driver without nvidia-drm exposes no DRM card + if command -v nvidia-smi &>/dev/null && nvidia-smi -L &>/dev/null; then + echo "nvidia" + return + fi + + # Fallback: check Vulkan devices + if command -v vulkaninfo &>/dev/null; then + vulkaninfo --summary 2>/dev/null | grep -qi "NVIDIA" && echo "nvidia" && return + vulkaninfo --summary 2>/dev/null | grep -qi "AMD" && echo "amd" && return + vulkaninfo --summary 2>/dev/null | grep -qi "Intel" && echo "intel" && return + fi + echo "unknown" } # Detect compositor/window manager detect_compositor() { - if [ "$XDG_SESSION_TYPE" = "wayland" ]; then - case "$XDG_CURRENT_DESKTOP" in - *KDE*) echo "kde" ;; - *GNOME*) echo "gnome" ;; - *XFCE*) echo "xfce" ;; - *MATE*) echo "mate" ;; - *) echo "other-wayland" ;; - esac - elif [ "$XDG_SESSION_TYPE" = "x11" ]; then - case "$XDG_CURRENT_DESKTOP" in - *KDE*) echo "kde-x11" ;; - *GNOME*) echo "gnome-x11" ;; - *) echo "other-x11" ;; - esac - else - echo "unknown" - fi + if [ "$XDG_SESSION_TYPE" = "wayland" ]; then + case "$XDG_CURRENT_DESKTOP" in + *KDE*) echo "kde" ;; + *GNOME*) echo "gnome" ;; + *XFCE*) echo "xfce" ;; + *MATE*) echo "mate" ;; + *) echo "other-wayland" ;; + esac + elif [ "$XDG_SESSION_TYPE" = "x11" ]; then + case "$XDG_CURRENT_DESKTOP" in + *KDE*) echo "kde-x11" ;; + *GNOME*) echo "gnome-x11" ;; + *) echo "other-x11" ;; + esac + else + echo "unknown" + fi } # Check if KDE HDR is enabled on primary monitor check_kde_hdr_enabled() { - if [ "$(detect_compositor)" != "kde" ]; then - echo "false" - return - fi - - # Check if kscreen-doctor is available - if ! command -v kscreen-doctor &>/dev/null; then - echo "false" - return - fi - - # Get primary output and check HDR status - local output - output=$(kscreen-doctor -o 2>/dev/null | grep -E "HDR|DDC" | head -1) - - if echo "$output" | grep -q "HDR: enabled"; then - echo "true" - else - echo "false" - fi + if [ "$(detect_compositor)" != "kde" ]; then + echo "false" + return + fi + + # Check if kscreen-doctor and jq are available + if ! command -v kscreen-doctor &>/dev/null || ! command -v jq &>/dev/null; then + echo "false" + return + fi + + # Primary output is the enabled one with priority 1. JSON output avoids the + # ANSI color codes kscreen-doctor -o emits even through a pipe. + local hdr + hdr=$(kscreen-doctor -j 2>/dev/null | + jq -r '[.outputs[]? | select(.enabled and .priority == 1)][0].hdr // false' 2>/dev/null) || true + + if [ "$hdr" = "true" ]; then + echo "true" + else + echo "false" + fi } # Main output main() { - cat <"$MOCK_DIR/kscreen-doctor" < [desktop] +assert_hdr() { + local label="$1" want="$2" fixture="$3" desktop="${4:-KDE}" got + printf '%s' "$fixture" >"$FIXTURE" + got=$(PATH="$MOCK_DIR:$PATH" XDG_SESSION_TYPE=wayland XDG_CURRENT_DESKTOP="$desktop" \ + bash "$SCRIPT" | jq -r '.kde_hdr_enabled') + + if [[ "$got" == "$want" ]]; then + printf " \033[32m✓\033[0m %s\n" "$label" + ((PASS++)) || true + else + printf " \033[31m✗\033[0m %s\n" "$label" + printf " want: %s got: %s\n" "$want" "$got" + ((FAIL++)) || true + fi +} + +echo "── check_kde_hdr_enabled ──" +assert_hdr "non-HDR secondary listed first, primary HDR on (issue #182)" true \ + '{"outputs":[{"name":"DP-2","enabled":true,"priority":2},{"name":"DP-3","enabled":true,"priority":1,"hdr":true}]}' +assert_hdr "primary HDR off, secondary HDR on" false \ + '{"outputs":[{"name":"DP-2","enabled":true,"priority":2,"hdr":true},{"name":"DP-3","enabled":true,"priority":1,"hdr":false}]}' +assert_hdr "single output without hdr key" false \ + '{"outputs":[{"name":"DP-1","enabled":true,"priority":1}]}' +assert_hdr "disabled output carrying priority 1 is ignored" false \ + '{"outputs":[{"name":"DP-1","enabled":false,"priority":1,"hdr":true}]}' +assert_hdr "invalid JSON" false 'not json' +assert_hdr "empty output" false '' +assert_hdr "non-KDE compositor" false \ + '{"outputs":[{"name":"DP-1","enabled":true,"priority":1,"hdr":true}]}' GNOME + +# Empty vulkaninfo and GPU-less nvidia-smi mocks so the no-card fallbacks are +# hermetic; NVIDIA_SMI_GPUS=1 makes nvidia-smi report a GPU +printf '#!/usr/bin/env bash\n' >"$MOCK_DIR/vulkaninfo" +printf '#!/usr/bin/env bash\n[ "${NVIDIA_SMI_GPUS:-0}" = 1 ]\n' >"$MOCK_DIR/nvidia-smi" +chmod +x "$MOCK_DIR/vulkaninfo" "$MOCK_DIR/nvidia-smi" + +# assert_gpu