#!/bin/bash # Release display pipes the kernel left enabled on unplugged connectors. # # After suspend/resume (or unplugging a USB-C display) i915 can leave a # connector "disconnected" but still "enabled" on a pipe that Hyprland has # forgotten. That pipe then blocks every other external output from # modesetting (black screens, or a ~5 s re-modeset loop). # # Fix: force the connector on so Hyprland sees it, disable it from Hyprland # (which releases the pipe), force it off, then return it to auto-detect. # Never `hyprctl reload` here: a reload re-enables the connector while it is # forced on and re-sticks the pipe. log() { echo "fix-stuck-displays: $*"; logger -t fix-stuck-displays -- "$*"; } wait_for() { # wait_for local n=$(( $1 * 4 )); shift for _ in $(seq "$n"); do "$@" && return 0; sleep 0.25; done return 1 } stuck=() for d in /sys/class/drm/card*-*; do [[ -f $d/status && -f $d/enabled ]] || continue [[ $d == *eDP* ]] && continue [[ $(<"$d/status") == disconnected && $(<"$d/enabled") == enabled ]] && stuck+=("$d") done if (( ${#stuck[@]} == 0 )); then log "no stuck connectors" exit 0 fi systemctl --user stop hyprmoncfgd for d in "${stuck[@]}"; do out=${d##*/}; out=${out#card*-} log "releasing $out" echo on | sudo -n tee "$d/status" >/dev/null wait_for 5 sh -c "hyprctl monitors all | grep -q '^Monitor $out '" || log "$out never appeared in Hyprland" hyprctl eval "hl.monitor({ output = \"$out\", disabled = true })" >/dev/null wait_for 5 grep -qx disabled "$d/enabled" || log "$out still enabled after disable" echo off | sudo -n tee "$d/status" >/dev/null sleep 1 echo detect | sudo -n tee "$d/status" >/dev/null log "$out now $(<"$d/status") $(<"$d/enabled")" done # Drop any low-refresh fallback hyprmoncfgd wrote while outputs were failing. rm -f ~/.config/hyprmoncfg/display-fallbacks.json # hyprmoncfgd re-applies the matching profile (modes, positions) on startup. systemctl --user start hyprmoncfgd