#!/usr/bin/env bash set -euo pipefail if ! command -v mmsg >/dev/null 2>&1; then echo "error: mmsg not found" >&2 exit 1 fi if ! command -v jq >/dev/null 2>&1; then echo "error: jq not found; install with: sudo pacman -S jq" >&2 exit 1 fi mode="${1:-all}" case "$mode" in all|single) ;; *) echo "usage: $(basename "$0") [all|single]" >&2 exit 2 ;; esac get_monitors_json() { local json json="$(mmsg get all-monitors 2>&1)" if ! jq -e '.monitors | type == "array"' >/dev/null 2>&1 <<<"$json"; then echo "error: failed to read monitors from mmsg:" >&2 echo "$json" >&2 exit 1 fi printf '%s\n' "$json" } get_outputs() { get_monitors_json | jq -r '.monitors[].name' } get_active_monitor() { local json="$1" local active active="$(jq -r '.monitors[] | select(.active == true) | .name' <<<"$json" | head -n1)" if [[ -n "$active" && "$active" != "null" ]]; then echo "$active" else jq -r '.monitors[0].name' <<<"$json" fi } get_active_tag() { local mon="$1" get_monitors_json | jq -r --arg mon "$mon" ' .monitors[] | select(.name == $mon) | (.active_tags[0] // 1) ' } get_tag_clients() { local mon="$1" local tag="$2" get_monitors_json | jq -r --arg mon "$mon" --argjson tag "$tag" ' .monitors[] | select(.name == $mon) | (.tags[] | select(.index == $tag) | .client_count) // 0 ' } get_total_clients() { local mon="$1" get_monitors_json | jq -r --arg mon "$mon" ' .monitors[] | select(.name == $mon) | [.tags[].client_count] | add // 0 ' } dispatch() { mmsg dispatch "$1" >/dev/null } json="$(get_monitors_json)" mapfile -t outputs < <(jq -r '.monitors[].name' <<<"$json") out_count="${#outputs[@]}" if (( out_count == 0 )); then echo "error: no monitors detected" >&2 exit 1 fi if (( out_count == 1 )); then echo "info: only one monitor detected by MangoWM, nothing to move" exit 0 fi if (( out_count > 2 )); then echo "error: script supports max 2 monitors, found ${out_count}" >&2 printf 'monitors:\n' >&2 printf ' %s\n' "${outputs[@]}" >&2 exit 2 fi active="$(get_active_monitor "$json")" target="" for out in "${outputs[@]}"; do if [[ "$out" != "$active" ]]; then target="$out" break fi done if [[ -z "$target" ]]; then echo "error: failed to determine target monitor" >&2 exit 1 fi if [[ "$mode" == "single" ]]; then echo "move-single: active=${active} target=${target}" dispatch "focusmon,${active}" dispatch "tagmon,${target},1" echo "move-single: done" exit 0 fi target_tag="$(get_active_tag "$target")" source_total="$(get_total_clients "$active")" if (( source_total == 0 )); then echo "info: no windows on active monitor (${active})" exit 0 fi echo "move-all: active=${active} target=${target} target_tag=${target_tag} total=${source_total}" moved=0 for tag in {1..9}; do while :; do before="$(get_tag_clients "$active" "$tag")" (( before > 0 )) || break dispatch "focusmon,${active}" # Select source tag. # If your Mango build uses tag masks instead of tag indexes for view, # this line may need changing. dispatch "view,${tag}" dispatch "tagcrossmon,${target_tag},${target}" dispatch "focusmon,${active}" || true after="$(get_tag_clients "$active" "$tag")" if (( after < before )); then (( moved += (before - after) )) else echo "warn: no progress on tag=${tag}; stopping this tag" break fi done done left="$(get_total_clients "$active")" echo "move-all: done moved=${moved} left_on_source=${left} from=${active} to=${target} tag=${target_tag}"