From 1cb7fbcdf207aa813e37b8d4370a0ec444e3f579 Mon Sep 17 00:00:00 2001 From: Zach Lowden Date: Thu, 6 Aug 2026 21:48:07 -0500 Subject: [PATCH 1/2] fix(rig-control): reliable brightness restore after sleep MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Three bugs causing dark screen after wake: 1. Race: background fade_blackout overwrites restore — kill the fade process (via PID file) before restoring; run fade loops foreground so kill stops everything cleanly; make do_wake() blocking 2. Saved brightness of 0 from stale state file — treat 0 as invalid, default to 60 3. DDC-CI flakiness — retry setvcp 3x with 0.5s backoff, tolerates individual failures in fade loops 4. schedule_restore used raw ddcutil (no retry, no 0-default) — now calls monitor-blackout.sh restore which has both 5. restore() cancels timer twice (before and after fade kill) to catch timers created by a fade completing between cancel and kill --- scripts/monitor-blackout.sh | 46 +++++++++++++++++++++++++++++-------- scripts/rig-control.sh | 2 +- 2 files changed, 38 insertions(+), 10 deletions(-) diff --git a/scripts/monitor-blackout.sh b/scripts/monitor-blackout.sh index 41412a1..f2353b2 100755 --- a/scripts/monitor-blackout.sh +++ b/scripts/monitor-blackout.sh @@ -20,6 +20,7 @@ set -euo pipefail DDC=$(command -v ddcutil) || { echo "ddcutil not found on PATH" >&2; exit 1; } UNIT=monitor-blackout-restore STATE="${XDG_RUNTIME_DIR:-/tmp}/monitor-blackout.state" # "bus:brightness" +FADE_PID_FILE="${XDG_RUNTIME_DIR:-/tmp}/monitor-blackout.fade.pid" # Number of fade steps and inter-step delay for fade transitions. # ~560ms per ddcutil call (hard floor at --sleep-multiplier 0.1), so 15 steps @@ -44,8 +45,14 @@ get_brightness() { # $1=bus -> current VCP 0x10 value "$DDC" --bus "$1" getvcp 10 --brief 2>/dev/null | awk '{print $4}' } -set_brightness() { # $1=bus $2=value - "$DDC" --bus "$1" setvcp 10 "$2" --noverify 2>/dev/null +set_brightness() { # $1=bus $2=value — retries 3x on DDC-CI failure (this LG panel is flaky) + local bus="$1" val="$2" attempt + for attempt in 1 2 3; do + "$DDC" --bus "$bus" setvcp 10 "$val" --noverify 2>/dev/null && return 0 + sleep 0.5 + done + echo "DDC-CI setvcp failed after 3 attempts on bus $bus (val=$val)" >&2 + return 1 } cancel_timer() { @@ -54,8 +61,11 @@ cancel_timer() { } schedule_restore() { # $1=bus $2=original_brightness $3=duration + # Write saved brightness so `restore` can read it; use the script itself + # (with retry + 0-default) instead of raw ddcutil. + printf '%s:%s\n' "$1" "$2" > "$STATE" systemd-run --user --unit="$UNIT" --on-active="$3" --timer-property=AccuracySec=1s \ - "$DDC" --bus "$1" setvcp 10 "$2" >/dev/null + "$0" restore >/dev/null } # --- actions --------------------------------------------------------------- @@ -76,10 +86,26 @@ blackout() { } restore() { + cancel_timer + # Kill any running background fade (sleep was triggered but wake came before it finished) + if [ -f "$FADE_PID_FILE" ]; then + local fpid + fpid=$(cat "$FADE_PID_FILE" 2>/dev/null || true) + rm -f "$FADE_PID_FILE" + if [ -n "$fpid" ] && kill -0 "$fpid" 2>/dev/null; then + kill "$fpid" 2>/dev/null || true + sleep 0.5 + fi + fi + # Cancel again — a fade that completes between first cancel and kill may have + # re-created the timer via schedule_restore. cancel_timer local bus="" cur="" [ -f "$STATE" ] && IFS=: read -r bus cur < "$STATE" || true - bus="${bus:-$(detect_bus || true)}"; cur="${cur:-60}" + bus="${bus:-$(detect_bus || true)}" + # Saved brightness of 0 means blackout read while monitor was already dark + # (race with prior fade) — fall back to a usable default instead of staying dark. + [ -n "$cur" ] && [ "$cur" != "0" ] || cur=60 [ -n "$bus" ] || { echo "no DDC/CI monitor detected" >&2; exit 1; } set_brightness "$bus" "$cur" rm -f "$STATE" @@ -90,9 +116,8 @@ fade_to_zero() { # $1=bus $2=from_brightness local bus="$1" from="$2" to=0 i val for (( i=0; i<=FADE_STEPS; i++ )); do val=$(( from + (to - from) * i / FADE_STEPS )) - set_brightness "$bus" "$val" & + set_brightness "$bus" "$val" sleep "$FADE_DELAY" - wait done } @@ -100,9 +125,8 @@ fade_from_zero() { # $1=bus $2=to_brightness local bus="$1" to="$2" from=0 i val for (( i=0; i<=FADE_STEPS; i++ )); do val=$(( from + (to - from) * i / FADE_STEPS )) - set_brightness "$bus" "$val" & + set_brightness "$bus" "$val" sleep "$FADE_DELAY" - wait done } @@ -113,9 +137,12 @@ fade_blackout() { cur=$(get_brightness "$bus") || true [ -n "${cur:-}" ] || { echo "could not read brightness on bus $bus" >&2; exit 1; } printf '%s:%s\n' "$bus" "$cur" > "$STATE" + echo "$$" > "$FADE_PID_FILE" fade_to_zero "$bus" "$cur" + rm -f "$FADE_PID_FILE" + cancel_timer schedule_restore "$bus" "$cur" "$dur" echo "Monitor faded to black (bus $bus, was $cur/100). Auto-restore in $dur — or: $0 restore" @@ -125,7 +152,8 @@ fade_restore() { cancel_timer local bus="" cur="" [ -f "$STATE" ] && IFS=: read -r bus cur < "$STATE" || true - bus="${bus:-$(detect_bus || true)}"; cur="${cur:-60}" + bus="${bus:-$(detect_bus || true)}" + [ -n "$cur" ] && [ "$cur" != "0" ] || cur=60 [ -n "$bus" ] || { echo "no DDC/CI monitor detected" >&2; exit 1; } fade_from_zero "$bus" "$cur" diff --git a/scripts/rig-control.sh b/scripts/rig-control.sh index b7da95a..eb86358 100755 --- a/scripts/rig-control.sh +++ b/scripts/rig-control.sh @@ -68,7 +68,7 @@ do_sleep() { do_wake() { echo "awake" > "$STATE_FILE" rgb_on - restore_bg + restore notify "Wake mode activated" } From 69363a093828a82569abc7f9137d37aff711a683 Mon Sep 17 00:00:00 2001 From: Zach Lowden Date: Thu, 6 Aug 2026 22:02:24 -0500 Subject: [PATCH 2/2] fix(rig-control): aggressive timer cancellation prevents stale restores Stale pre-fix timers (raw ddcutil, same unit name) were still firing after restore cancelled the current timer. Fix: - cancel_timer: also kill running service instances, reset failed state - schedule_restore: call cancel_timer before creating new timer - Both changes prevent old timers from racing with new ones --- scripts/monitor-blackout.sh | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/scripts/monitor-blackout.sh b/scripts/monitor-blackout.sh index f2353b2..59fd776 100755 --- a/scripts/monitor-blackout.sh +++ b/scripts/monitor-blackout.sh @@ -57,6 +57,7 @@ set_brightness() { # $1=bus $2=value — retries 3x on DDC-CI failure (this LG cancel_timer() { systemctl --user stop "${UNIT}.timer" 2>/dev/null || true + systemctl --user kill "${UNIT}.service" 2>/dev/null || true systemctl --user reset-failed "${UNIT}.service" "${UNIT}.timer" 2>/dev/null || true } @@ -64,6 +65,9 @@ schedule_restore() { # $1=bus $2=original_brightness $3=duration # Write saved brightness so `restore` can read it; use the script itself # (with retry + 0-default) instead of raw ddcutil. printf '%s:%s\n' "$1" "$2" > "$STATE" + # Aggressively cancel any existing timer — stale pre-fix timers share the + # same unit name and can race with this creation. + cancel_timer systemd-run --user --unit="$UNIT" --on-active="$3" --timer-property=AccuracySec=1s \ "$0" restore >/dev/null }