diff --git a/sway/.config/sway/config b/sway/.config/sway/config index 5dbedb8..f683eb6 100644 --- a/sway/.config/sway/config +++ b/sway/.config/sway/config @@ -1,10 +1,11 @@ exec dbus-update-activation-environment --systemd WAYLAND_DISPLAY XDG_CURRENT_DESKTOP=sway exec_always autotiling -l 2 exec_always ~/.config/sway/scripts/workspace-colors.py -exec_always eww daemon && (eww open bar0 || true) && (eww open bar1 || true) +exec_always ~/.config/sway/scripts/eww-bars.sh exec_always mako exec /usr/libexec/polkit-mate-authentication-agent-1 +exec_always ~/.config/sway/scripts/watch-outputs.sh exec flatpak run org.signal.Signal exec swayidle -w \ diff --git a/sway/.config/sway/scripts/eww-bars.sh b/sway/.config/sway/scripts/eww-bars.sh new file mode 100755 index 0000000..d28939f --- /dev/null +++ b/sway/.config/sway/scripts/eww-bars.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# Reconcile the eww bars with the currently-connected sway outputs. +# +# Mapping: eww window -> output (connector name) it must live on. +# Each defwindow in ~/.config/eww/eww.yuck is bound to the SAME name via +# `:monitor ""`, so a bar's workspace list and its physical placement +# can never drift apart. (The old numeric `:monitor 0/1` indices were not +# stable across sway reloads / display hotplug, which is what mixed up the +# bars: e.g. the internal panel showed the external display's workspaces.) +# +# Usage: +# eww-bars.sh Open bars whose monitor is connected, close the rest. +# Idempotent and flicker-free on a normal sway reload. +# eww-bars.sh --force Also re-place already-open bars (close + reopen). +# Use right after an external display is (dis)connected, +# so a stale layer-surface can't keep a bar hidden. +# +# To support another monitor, just add a `window -> connector` pair below and +# a matching `defwindow` in eww.yuck. + +set -u + +declare -A BARS=( + [bar0]="HDMI-A-1" # external + [bar1]="eDP-1" # internal / built-in +) + +force=0 +[[ "${1:-}" == "--force" ]] && force=1 + +eww daemon >/dev/null 2>&1 || true + +output_active() { + swaymsg -t get_outputs 2>/dev/null \ + | jq -e --arg n "$1" 'any(.[]; .name == $n and .active)' >/dev/null 2>&1 +} + +for win in "${!BARS[@]}"; do + out="${BARS[$win]}" + if output_active "$out"; then + (( force )) && eww close "$win" >/dev/null 2>&1 + eww open "$win" >/dev/null 2>&1 || true + else + eww close "$win" >/dev/null 2>&1 || true + fi +done diff --git a/sway/.config/sway/scripts/watch-outputs.sh b/sway/.config/sway/scripts/watch-outputs.sh new file mode 100755 index 0000000..9a59bb8 --- /dev/null +++ b/sway/.config/sway/scripts/watch-outputs.sh @@ -0,0 +1,77 @@ +#!/usr/bin/env bash +# Watches sway outputs over IPC (no polling) and keeps the eww bars and the +# 1-3 workspaces in sync with the external display. +# +# Launched from the sway config with `exec_always` + the flock guard below, so: +# - a sway *reload* restarts it if it ever died (self-healing), and +# - it never spawns duplicate watchers (the second instance exits at flock). +# +# When a non-built-in output appears, workspaces 1-3 are moved to it and sway is +# reloaded; on any output change the eww bars are re-placed (--force) so a bar +# whose monitor was unplugged/replugged is rebuilt on the correct display. + +# --- single-instance guard ------------------------------------------------- +LOCK="${XDG_RUNTIME_DIR:-/tmp}/watch-outputs.lock" +exec 9>"$LOCK" +flock -n 9 || exit 0 # another instance is already running + +BARS_SCRIPT="$HOME/.config/sway/scripts/eww-bars.sh" + +declare -A known_outputs + +list_outputs() { swaymsg -t get_outputs | jq -r '.[].name'; } + +seed_known_outputs() { + while IFS= read -r name; do + known_outputs[$name]=1 + done < <(list_outputs) +} + +on_output_event() { + local -A current + while IFS= read -r name; do + current[$name]=1 + done < <(list_outputs) + + local changed=0 + + # Detect newly connected outputs + for name in "${!current[@]}"; do + [[ -v "known_outputs[$name]" ]] && continue + known_outputs[$name]=1 + changed=1 + + # Skip built-in display + [[ "$name" == eDP* ]] && continue + + # External display connected — claim workspaces 1-3 and reload so the + # per-output position/scale rules (hdmi-setup.sh) are reapplied. + sleep 0.5 + for i in {1..3}; do + swaymsg "workspace $i; move workspace to output $name" + done + swaymsg reload + done + + # Detect disconnected outputs + for name in "${!known_outputs[@]}"; do + [[ -v "current[$name]" ]] && continue + unset "known_outputs[$name]" + changed=1 + done + + # Re-place the eww bars onto whatever is connected now. --force rebuilds a + # stale layer-surface left behind by an unplug so the bar reappears. + (( changed )) && "$BARS_SCRIPT" --force +} + +seed_known_outputs +"$BARS_SCRIPT" # reconcile once at startup + +# Reconnect loop — re-subscribes if the IPC connection ever drops. +while true; do + swaymsg -t subscribe '["output"]' --monitor | while IFS= read -r _; do + on_output_event + done + sleep 2 +done