Fix multi-monitor workspace routing regression, add laptop auto-detect
- Restore desktop DP-3=primary/DP-2=secondary workspace mapping that broke when workspace routing switched from name-matching to raw geometric ordering. - Extract shared laptop/desktop detection + output ranking into lib-outputs.sh (battery presence selects the profile automatically), sourced by both select-workspace.sh and the new apply-outputs.sh, so keybind routing and monitor placement can never disagree. - Add apply-outputs.sh: on the laptop, automatically positions whatever external display is plugged in (and re-runs on hotplug via swaymsg -t subscribe), making it primary. No-op on the desktop, which keeps its static fixed-arrangement config.
This commit is contained in:
@@ -16,11 +16,15 @@ output * bg ~/.config/sway/wallpapers/blue-horizon.svg fill
|
|||||||
# Desktop-only dual-monitor arrangement: DP-3 is the centered primary
|
# Desktop-only dual-monitor arrangement: DP-3 is the centered primary
|
||||||
# display, DP-2 sits immediately to its left. Sway silently ignores
|
# display, DP-2 sits immediately to its left. Sway silently ignores
|
||||||
# `output` directives for names that don't exist, so this is a no-op on
|
# `output` directives for names that don't exist, so this is a no-op on
|
||||||
# the single-monitor laptop (its internal panel, e.g. eDP-1, is untouched
|
# the laptop, where scripts/apply-outputs.sh instead positions whatever
|
||||||
# and simply gets everything). Workspace-to-output routing is not pinned
|
# external display gets plugged in dynamically (see that script and
|
||||||
# here; scripts/select-workspace.sh assigns workspaces to whichever
|
# scripts/lib-outputs.sh for the shared laptop/desktop detection logic).
|
||||||
# outputs are actually connected, in left-to-right order, so it adapts
|
#
|
||||||
# automatically on both machines without any per-host config.
|
# Workspace-to-output routing isn't pinned here either way:
|
||||||
|
# scripts/select-workspace.sh (shared with apply-outputs.sh) assigns
|
||||||
|
# workspaces 1-10/11-20/... to whichever outputs are connected, ranked
|
||||||
|
# primary-first, so both keybinds and monitor placement always agree on
|
||||||
|
# which output is "first" without any per-host config.
|
||||||
output DP-3 position 0 0
|
output DP-3 position 0 0
|
||||||
output DP-2 position -2560 0
|
output DP-2 position -2560 0
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
exec_always --no-startup-id sh -c 'pkill waybar; exec waybar'
|
exec_always --no-startup-id sh -c 'pkill waybar; exec waybar'
|
||||||
|
exec_always --no-startup-id sh -c 'pkill -f apply-outputs.sh; exec ~/.config/sway/scripts/apply-outputs.sh'
|
||||||
exec --no-startup-id mako
|
exec --no-startup-id mako
|
||||||
exec --no-startup-id nm-applet --indicator
|
exec --no-startup-id nm-applet --indicator
|
||||||
exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
|
exec --no-startup-id /usr/lib/polkit-gnome/polkit-gnome-authentication-agent-1
|
||||||
|
|||||||
Executable
+29
@@ -0,0 +1,29 @@
|
|||||||
|
#!/usr/bin/env bash
|
||||||
|
# Laptop-only dynamic monitor placement: whenever a display is plugged or
|
||||||
|
# unplugged, arrange active outputs left-to-right in output_order() order
|
||||||
|
# (external display(s) first, internal panel last), so a newly connected
|
||||||
|
# external monitor gets a sensible position automatically without any
|
||||||
|
# manual config. On the desktop this exits immediately — that monitor
|
||||||
|
# pair has a fixed physical arrangement set statically in
|
||||||
|
# conf.d/10-appearance.conf.
|
||||||
|
set -Eeuo pipefail
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib-outputs.sh"
|
||||||
|
|
||||||
|
is_laptop || exit 0
|
||||||
|
|
||||||
|
apply_layout() {
|
||||||
|
local outputs_json x=0 name width
|
||||||
|
outputs_json=$(swaymsg -t get_outputs -r)
|
||||||
|
mapfile -t ordered < <(output_order)
|
||||||
|
for name in "${ordered[@]}"; do
|
||||||
|
width=$(jq -r --arg n "$name" '.[] | select(.name==$n) | .rect.width' <<<"$outputs_json")
|
||||||
|
swaymsg output "$name" position "$x" 0 >/dev/null 2>&1 || true
|
||||||
|
x=$((x + width))
|
||||||
|
done
|
||||||
|
}
|
||||||
|
|
||||||
|
apply_layout
|
||||||
|
swaymsg -t subscribe -m '["output"]' | while read -r _; do
|
||||||
|
apply_layout
|
||||||
|
done
|
||||||
@@ -0,0 +1,55 @@
|
|||||||
|
# Shared helpers for arranging outputs and routing workspaces across both
|
||||||
|
# machines: the laptop (whatever external display happens to be plugged
|
||||||
|
# in, if any) and the desktop (a fixed DP-3/DP-2 pair). Sourced by
|
||||||
|
# apply-outputs.sh and select-workspace.sh so both always agree on which
|
||||||
|
# output is "primary" and in what order the rest follow.
|
||||||
|
|
||||||
|
is_laptop() {
|
||||||
|
local supply
|
||||||
|
for supply in /sys/class/power_supply/*/; do
|
||||||
|
[[ -r "${supply}type" ]] || continue
|
||||||
|
[[ "$(<"${supply}type")" == Battery ]] && return 0
|
||||||
|
done
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
is_internal_panel() {
|
||||||
|
case "$1" in
|
||||||
|
eDP-*|LVDS-*|DSI-*) return 0 ;;
|
||||||
|
*) return 1 ;;
|
||||||
|
esac
|
||||||
|
}
|
||||||
|
|
||||||
|
# Prints the names of currently active outputs, one per line, ordered
|
||||||
|
# primary-first:
|
||||||
|
# - laptop: any external display(s), in the order sway reports them,
|
||||||
|
# then the internal panel last (so a docked external is
|
||||||
|
# always primary; undocked, the panel is the only entry).
|
||||||
|
# - desktop: DP-3, then DP-2, then anything else alphabetically as a
|
||||||
|
# fallback for an unexpected output.
|
||||||
|
output_order() {
|
||||||
|
local outputs_json name
|
||||||
|
outputs_json=$(swaymsg -t get_outputs -r)
|
||||||
|
mapfile -t active < <(jq -r '.[] | select(.active) | .name' <<<"$outputs_json")
|
||||||
|
|
||||||
|
local ordered=()
|
||||||
|
if is_laptop; then
|
||||||
|
local externals=() internals=()
|
||||||
|
for name in "${active[@]}"; do
|
||||||
|
if is_internal_panel "$name"; then internals+=("$name"); else externals+=("$name"); fi
|
||||||
|
done
|
||||||
|
ordered=("${externals[@]}" "${internals[@]}")
|
||||||
|
else
|
||||||
|
local preferred=(DP-3 DP-2) p seen
|
||||||
|
for p in "${preferred[@]}"; do
|
||||||
|
for name in "${active[@]}"; do [[ "$name" == "$p" ]] && ordered+=("$name"); done
|
||||||
|
done
|
||||||
|
for name in "${active[@]}"; do
|
||||||
|
seen=false
|
||||||
|
local o
|
||||||
|
for o in "${ordered[@]}"; do [[ "$o" == "$name" ]] && seen=true; done
|
||||||
|
"$seen" || ordered+=("$name")
|
||||||
|
done
|
||||||
|
fi
|
||||||
|
printf '%s\n' "${ordered[@]}"
|
||||||
|
}
|
||||||
@@ -1,5 +1,7 @@
|
|||||||
#!/usr/bin/env bash
|
#!/usr/bin/env bash
|
||||||
set -Eeuo pipefail
|
set -Eeuo pipefail
|
||||||
|
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
source "$SCRIPT_DIR/lib-outputs.sh"
|
||||||
|
|
||||||
slot=${1:-}
|
slot=${1:-}
|
||||||
action=${2:-select}
|
action=${2:-select}
|
||||||
@@ -9,15 +11,18 @@ if [[ ! "$slot" =~ ^([1-9]|10)$ ]]; then
|
|||||||
exit 2
|
exit 2
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# Outputs are ordered left-to-right by position, not by name, so this works
|
mapfile -t ordered < <(output_order)
|
||||||
# unmodified whether there is one output (laptop) or several (desktop):
|
focused=$(swaymsg -t get_outputs -r | jq -r '.[] | select(.focused) | .name')
|
||||||
# the focused output's rank in that order selects its block of 10 workspaces.
|
|
||||||
outputs=$(swaymsg -t get_outputs -r | jq -c 'sort_by(.rect.x)')
|
index=-1
|
||||||
index=$(jq -r '[.[] | .focused] | index(true)' <<<"$outputs")
|
for i in "${!ordered[@]}"; do
|
||||||
if [[ "$index" == null ]]; then
|
[[ "${ordered[$i]}" == "$focused" ]] && index=$i && break
|
||||||
printf 'No focused output found\n' >&2
|
done
|
||||||
|
if ((index < 0)); then
|
||||||
|
printf 'Focused output %s is not currently tracked\n' "$focused" >&2
|
||||||
exit 1
|
exit 1
|
||||||
fi
|
fi
|
||||||
|
|
||||||
workspace=$((slot + index * 10))
|
workspace=$((slot + index * 10))
|
||||||
|
|
||||||
case "$action" in
|
case "$action" in
|
||||||
|
|||||||
Reference in New Issue
Block a user