headless-primary: relocate workspaces stranded on a mirrored output

On swap-back Hyprland brings DP-1 up normal, binds a workspace to it, then apply
mirrors DP-1 — trapping that workspace on a layout-excluded mirror (unreachable;
symptom: 'can't make workspace 1 show'). apply now moves any workspace off a
mirrored output onto the headless primary. Verified the heal operation
(move-off-mirror) manually.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
Levi Woodard
2026-07-30 16:10:29 -06:00
parent 0d6da8c24a
commit 0fe38e9797
2 changed files with 24 additions and 5 deletions

View File

@@ -113,18 +113,32 @@ apply() {
log "DP-1 mirroring $head"
fi
# 4. Rescue orphaned workspaces (monitorID == -1) onto the headless primary.
# 4. Relocate any workspace that lives on a MIRRORED output onto the headless
# primary. A mirror is excluded from the layout, so a workspace bound to it
# is trapped and unreachable — exactly what happens on swap-back when
# Hyprland brings DP-1 up normal, binds a workspace to it, then we mirror
# it. (tostring guards mirrorOf being null | "None" | a numeric id.)
local mon ws
for mon in $(hyprctl monitors all -j \
| jq -r '.[] | select(((.mirrorOf // "None") | tostring | ascii_downcase) != "none") | .name'); do
[[ "$mon" == "$head" ]] && continue
for ws in $(hyprctl workspaces -j | jq -r --arg m "$mon" '.[] | select(.monitor==$m) | .id'); do
log "relocating workspace $ws off mirror $mon -> $head"
hyprctl dispatch moveworkspacetomonitor "$ws $head" >/dev/null 2>&1 || true
done
done
# 5. Rescue orphaned workspaces (monitorID == -1) onto the headless primary.
# Narrow by design — a healthy second monitor (DP-2) is left alone.
local ws
for ws in $(hyprctl workspaces -j | jq -r '.[] | select(.monitorID == -1) | .id'); do
log "rescuing orphaned workspace $ws -> $head"
hyprctl dispatch moveworkspacetomonitor "$ws $head" >/dev/null 2>&1 || true
done
# 5. Rescue any floating windows stranded off-screen by the topology change.
# 6. Rescue any floating windows stranded off-screen by the topology change.
rescue_offscreen_windows
# 6. Keep Sunshine's output_name pointed at the live headless name.
# 7. Keep Sunshine's output_name pointed at the live headless name.
if [[ -f "$CONF" ]] && grep -qF '# managed-by: omarchy-moonlight' "$CONF"; then
local cur; cur="$(awk '/^output_name = / {print $3; exit}' "$CONF" 2>/dev/null || true)"
if [[ "$cur" != "$head" ]]; then