fix(docking): make workspace and lid transitions atomic
All checks were successful
Check / eval (push) Successful in 3m19s
All checks were successful
Check / eval (push) Successful in 3m19s
Restore the old flake's external-only workspace handoff, protect closed-lid cable removal with a verified low-level logind inhibitor, and use monitor-added as the settled audio reprobe boundary. Verified: V2 — nix flake check --no-build; docking-ux, option-docs, and template-sot checks; KVM tools/monitor-fallback.nix lifecycle test. V3 pending: closed-lid BenQ round 4 in agent/HARDWARE-QUEUE.md.
This commit is contained in:
@@ -15,14 +15,6 @@ let
|
||||
|
||||
px = n: mkLiteral "${toString n}px";
|
||||
|
||||
# Dock-class audio sinks (HDMI/DP/USB) — the same regex data the
|
||||
# WirePlumber priority rules and the dock-audio watcher consume
|
||||
# (modules/nixos/dock-audio-rules.nix is the single source).
|
||||
dockSinkRe = lib.concatStringsSep "|"
|
||||
(map (m: lib.removePrefix "~" m."node.name")
|
||||
(lib.concatMap (r: r.matches)
|
||||
(import ../nixos/dock-audio-rules.nix)."monitor.alsa.rules"));
|
||||
|
||||
# Per-theme override probe (same convention as waybar.css).
|
||||
rasiOverride = cfg.themesDir + "/${t.slug}/rofi.rasi";
|
||||
hasRasiOverride = builtins.pathExists rasiOverride;
|
||||
@@ -507,24 +499,16 @@ ${themeRows}
|
||||
if [ ! -e "''${XDG_RUNTIME_DIR:-/run/user/$(id -u)}/pulse/native" ]; then
|
||||
notify-send "Audio" "No PipeWire/PulseAudio socket (nomarchy.audio off?)."; exit 0
|
||||
fi
|
||||
# Docked quick row: the first dock-class sink (HDMI/DP/USB —
|
||||
# dockSinkRe, single source with the wireplumber rules) that is
|
||||
# not already the default. One pick sends output there; the
|
||||
# dock-audio watcher normally does this on plug, so this row is
|
||||
# the manual override / catch-up. pactl by store path — it is
|
||||
# not on PATH (PipeWire stack, wpctl only).
|
||||
sinks=$(${pkgs.pulseaudio}/bin/pactl --format=json list sinks 2>/dev/null || true)
|
||||
# The helper owns dock-class detection + availability (regexes come
|
||||
# from dock-audio-rules.nix once). This row is the manual catch-up.
|
||||
candidate=
|
||||
command -v nomarchy-dock-audio >/dev/null 2>&1 \
|
||||
&& candidate=$(nomarchy-dock-audio candidates 2>/dev/null \
|
||||
| ${pkgs.coreutils}/bin/head -n 1)
|
||||
dockname=''${candidate%%"$TAB"*}
|
||||
dockdesc=''${candidate#*"$TAB"}
|
||||
cursink=$(${pkgs.pulseaudio}/bin/pactl get-default-sink 2>/dev/null || true)
|
||||
dockname=$(printf '%s' "$sinks" | jq -r --arg cur "$cursink" --arg re '${dockSinkRe}' \
|
||||
'[.[]
|
||||
| select((.name | test("^(" + $re + ")$")) and .name != $cur)
|
||||
| select(
|
||||
((.ports // []) | length) == 0
|
||||
or any(.ports[]?; (.availability // "unknown") != "not available")
|
||||
)][0].name // empty')
|
||||
dockdesc=
|
||||
[ -n "$dockname" ] && dockdesc=$(printf '%s' "$sinks" \
|
||||
| jq -r --arg n "$dockname" '[.[] | select(.name == $n)][0].description // empty')
|
||||
[ "$dockname" = "$cursink" ] && dockname=
|
||||
choice=$( {
|
||||
[ -n "$dockname" ] && row "Send output → ''${dockdesc:-$dockname}" video-display
|
||||
row "Output device" audio-volume-high
|
||||
@@ -534,8 +518,7 @@ ${themeRows}
|
||||
case "$choice" in
|
||||
"$BACK") exec "$0" system ;;
|
||||
"Send output"*)
|
||||
${pkgs.pulseaudio}/bin/pactl set-default-sink "$dockname" \
|
||||
&& notify-send "Audio" "Output → ''${dockdesc:-$dockname}" ;;
|
||||
nomarchy-dock-audio select menu ;;
|
||||
*Output*) exec rofi-pulse-select sink ;;
|
||||
*Input*) exec rofi-pulse-select source ;;
|
||||
esac ;;
|
||||
@@ -599,12 +582,11 @@ ${themeRows}
|
||||
if [ -n "$profs" ] || [ "$nActive" -gt 1 ] || [ -n "$offmon" ]; then
|
||||
name=$( {
|
||||
[ -n "$profs" ] && printf 'Profiles ›\n'
|
||||
# Dock mode deliberately keeps the internal panel logically
|
||||
# active as the unplug fallback. Soft-disabling it can leave
|
||||
# Hyprland with zero outputs and kill the whole session before a
|
||||
# user-space rescue runs. Move its workspaces and focus instead.
|
||||
# Dock mode performs an atomic workspace handoff and only then
|
||||
# disables the internal panel. The always-on hotplug watcher
|
||||
# holds logind's lid-switch inhibitor across the reverse path.
|
||||
[ "$nActive" -gt 1 ] && [ -n "$internal" ] && [ -n "$external" ] \
|
||||
&& printf 'Dock mode · everything → %s (safe unplug)\n' "$external"
|
||||
&& printf 'Dock mode · external only → %s\n' "$external"
|
||||
[ -n "$offmon" ] && printf 'Screen on · %s\n' "$offmon"
|
||||
[ "$nActive" -gt 1 ] && printf 'Move workspace · → next monitor\n'
|
||||
[ "$nActive" -eq 2 ] && printf 'Swap workspaces · %s ⇄ %s\n' \
|
||||
@@ -617,20 +599,17 @@ ${themeRows}
|
||||
[ "$name" = "Profiles ›" ] && exec "$0" display-profile
|
||||
case "$name" in
|
||||
"Dock mode"*)
|
||||
moved=0
|
||||
while read -r ws; do
|
||||
[ -n "$ws" ] || continue
|
||||
hyprctl dispatch moveworkspacetomonitor "$ws" "$external" >/dev/null 2>&1 \
|
||||
&& moved=$((moved+1))
|
||||
done < <(hyprctl workspaces -j | jq -r --arg m "$internal" \
|
||||
'.[] | select(.monitor == $m and .id > 0) | .id')
|
||||
hyprctl dispatch focusmonitor "$external" >/dev/null 2>&1
|
||||
notify-send "Display" "Dock mode — $moved workspace(s) moved to $external; laptop panel kept as the unplug fallback."
|
||||
if nomarchy-display-transition dock "$internal" "$external" \
|
||||
"$external, preferred, auto, 1"; then
|
||||
notify-send "Display" "Dock mode — every laptop workspace moved to $external; laptop panel off."
|
||||
else
|
||||
notify-send "Display" "Dock transition failed; the previous layout was kept where possible."
|
||||
fi
|
||||
exit 0 ;;
|
||||
"Screen on"*)
|
||||
# preferred/auto/auto is good enough live; the declared
|
||||
# preferred/auto/1 is the known-good live fallback; the declared
|
||||
# scale/position come back at the next relogin/switch.
|
||||
if hyprctl keyword monitor "$offmon,preferred,auto,auto" >/dev/null 2>&1; then
|
||||
if nomarchy-display-transition enable "$offmon"; then
|
||||
notify-send "Display" "$offmon is back on."
|
||||
else
|
||||
notify-send "Display" "Could not enable $offmon."
|
||||
|
||||
Reference in New Issue
Block a user