Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions bin/omarchy-hw-nvidia-suspended
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#!/bin/bash

# omarchy:summary=Detect whether every NVIDIA GPU is runtime-suspended, so it is driving no display.

# Read the cached sysfs power state rather than lspci or NVML, both of which
# resume a runtime-suspended GPU just by asking after it.
pci_devices_path="${OMARCHY_PCI_DEVICES_PATH:-/sys/bus/pci/devices}"

shopt -s nullglob

found=false
for device in "$pci_devices_path"/*; do
[[ $(< "$device/vendor") == "0x10de" ]] || continue
[[ $(< "$device/class") == 0x03* ]] || continue

found=true
[[ -r $device/power/runtime_status && $(< "$device/power/runtime_status") == "suspended" ]] || exit 1
done

[[ $found == "true" ]]
27 changes: 27 additions & 0 deletions bin/omarchy-launch-activity
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#!/bin/bash

# omarchy:summary=Launch Activity (btop) without waking a runtime-suspended NVIDIA GPU

# btop initialises NVML at startup whenever shown_gpus lists nvidia, GPU box
# or not, and that alone resumes a suspended discrete GPU on hybrid laptops
# for as long as Activity stays open. Leave nvidia out of the list while the
# card sleeps, and put it back once the card is awake and worth watching.
btop_conf="$HOME/.config/btop/btop.conf"

if [[ -w $btop_conf ]] && shown=$(sed -n 's/^shown_gpus = "\(.*\)"$/\1/p' "$btop_conf") && [[ -n $shown ]]; then
read -r -a vendors <<<"$shown"
wanted=()
for vendor in "${vendors[@]}"; do
[[ $vendor == "nvidia" ]] || wanted+=("$vendor")
done

if ! omarchy-hw-nvidia-suspended; then
wanted=("nvidia" "${wanted[@]}")
fi

if [[ ${wanted[*]} != "$shown" ]]; then
sed -i "s/^shown_gpus = .*/shown_gpus = \"${wanted[*]}\"/" "$btop_conf"
fi
fi

exec omarchy-launch-tui btop
2 changes: 1 addition & 1 deletion default/hypr/bindings/utilities.lua
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ o.bind("SUPER + CTRL + D", "Display", "omarchy-shell shell toggle omarchy.monito
o.bind("SUPER + CTRL + ALT + D", "Calendar", "omarchy-shell shell toggle omarchy.clock")
o.bind("SUPER + CTRL + W", "Network", "omarchy-shell shell toggle omarchy.network")
o.bind("SUPER + CTRL + P", "Power", "omarchy-shell shell toggle omarchy.power")
o.bind("SUPER + CTRL + T", "Activity", { tui = "btop" })
o.bind("SUPER + CTRL + T", "Activity", { omarchy = "activity" })

-- The letters above name a panel; the numbers count them. 1 is the leftmost
-- panel in the bar's right section, and a widget with no panel of its own (the
Expand Down
50 changes: 50 additions & 0 deletions test/shell.d/hw-nvidia-suspended-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
#!/bin/bash

set -euo pipefail

source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"

tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT

# Each argument is "vendor:class:runtime_status"; an empty status omits the file.
write_pci_devices() {
rm -rf "$tmp_dir/devices"
mkdir -p "$tmp_dir/devices"

local index=0 spec
for spec in "$@"; do
local slot vendor class status
slot=$(printf '0000:%02x:00.0' "$index")
IFS=: read -r vendor class status <<<"$spec"
mkdir -p "$tmp_dir/devices/$slot/power"
printf '%s\n' "$vendor" >"$tmp_dir/devices/$slot/vendor"
printf '%s\n' "$class" >"$tmp_dir/devices/$slot/class"
[[ -z $status ]] || printf '%s\n' "$status" >"$tmp_dir/devices/$slot/power/runtime_status"
index=$((index + 1))
done
}

detects() {
OMARCHY_PCI_DEVICES_PATH="$tmp_dir/devices" "$ROOT/bin/omarchy-hw-nvidia-suspended"
}

write_pci_devices "0x1002:0x030000:active" "0x10de:0x030200:suspended"
detects || fail "a runtime-suspended NVIDIA GPU is detected as suspended"
pass "a runtime-suspended NVIDIA GPU is detected as suspended"

write_pci_devices "0x1002:0x030000:active" "0x10de:0x030200:active"
detects && fail "an active NVIDIA GPU is not reported as suspended"
pass "an active NVIDIA GPU is not reported as suspended"

write_pci_devices "0x10de:0x030200:suspended" "0x10de:0x030200:active"
detects && fail "one awake NVIDIA GPU keeps the machine out of the suspended state"
pass "one awake NVIDIA GPU keeps the machine out of the suspended state"

write_pci_devices "0x10de:0x030200:"
detects && fail "an NVIDIA GPU without runtime power management is not reported as suspended"
pass "an NVIDIA GPU without runtime power management is not reported as suspended"

write_pci_devices "0x1002:0x030000:active" "0x10de:0x040300:suspended"
detects && fail "a machine with no NVIDIA display device is not reported as suspended"
pass "a machine with no NVIDIA display device is not reported as suspended"
58 changes: 58 additions & 0 deletions test/shell.d/launch-activity-test.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
#!/bin/bash

set -euo pipefail

source "$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)/base-test.sh"

tmp_dir=$(mktemp -d)
trap 'rm -rf "$tmp_dir"' EXIT

mock_bin="$tmp_dir/bin"
test_home="$tmp_dir/home"
btop_conf="$test_home/.config/btop/btop.conf"
mkdir -p "$mock_bin" "$(dirname "$btop_conf")"

cat >"$mock_bin/omarchy-hw-nvidia-suspended" <<'STUB'
#!/bin/bash
[[ ${OMARCHY_TEST_NVIDIA_SUSPENDED:-false} == "true" ]]
STUB
cat >"$mock_bin/omarchy-launch-tui" <<'STUB'
#!/bin/bash
printf '%s\n' "$*" >"$OMARCHY_TEST_LAUNCH_LOG"
STUB
chmod +x "$mock_bin"/*

launch() {
HOME="$test_home" OMARCHY_TEST_LAUNCH_LOG="$tmp_dir/launch.log" PATH="$mock_bin:$PATH" "$ROOT/bin/omarchy-launch-activity"
}

shown_gpus() {
sed -n 's/^shown_gpus = "\(.*\)"$/\1/p' "$btop_conf"
}

write_conf() {
printf '#* Set which GPU vendors to show.\nshown_gpus = "%s"\n\ncustom_gpu_name0 = ""\n' "$1" >"$btop_conf"
}

write_conf "nvidia amd intel"
OMARCHY_TEST_NVIDIA_SUSPENDED=true launch
[[ $(shown_gpus) == "amd intel" ]] || fail "a suspended NVIDIA GPU is left out of shown_gpus" "$(<"$btop_conf")"
[[ $(<"$tmp_dir/launch.log") == "btop" ]] || fail "Activity still launches btop"
pass "a suspended NVIDIA GPU is left out of shown_gpus"

OMARCHY_TEST_NVIDIA_SUSPENDED=false launch
[[ $(shown_gpus) == "nvidia amd intel" ]] || fail "an awake NVIDIA GPU is put back into shown_gpus" "$(<"$btop_conf")"
pass "an awake NVIDIA GPU is put back into shown_gpus"

write_conf "amd"
OMARCHY_TEST_NVIDIA_SUSPENDED=true launch
[[ $(shown_gpus) == "amd" ]] || fail "a list without nvidia is left alone" "$(<"$btop_conf")"
grep -q '^custom_gpu_name0 = ""$' "$btop_conf" || fail "the rest of the config is untouched"
pass "a list without nvidia is left alone"

rm "$btop_conf"
: >"$tmp_dir/launch.log"
OMARCHY_TEST_NVIDIA_SUSPENDED=true launch
[[ $(<"$tmp_dir/launch.log") == "btop" ]] || fail "Activity launches without a btop config"
[[ ! -e $btop_conf ]] || fail "no config is invented when none exists"
pass "Activity launches without a btop config"