#!/usr/bin/env bash # # monitor setup script using xrandr and fzf to manage multiple monitors and/or # set resolutions and refresh rates. command -v xrandr &>/dev/null || exit 1 command -v fzf &>/dev/null || exit 1 xrandr_q="$(xrandr -q)" # Get outputs currently in use as regex matching either one (e.g. `HDMI-0|DP-0`) in_use="$( <<<"$xrandr_q" \ tac | # Reverse for non-greedy matches sed -En '/\*/,/ connected/p' | # lines from selected config to it's name grep -Po '^.*(?= connected)' | # only the name tr '\n' '|' # format into regex )" in_use="${in_use%|}" # remove trailing `|` # Get all outputs in a table in the form ` [*]` formatted="$(<<<"$xrandr_q" \ grep -wA1 connected | # output line and highest res awk '{ print $1 }' | # only name and resolution grep -ve -- | # remove grep's match separator paste - - | # join name and res sed -E "s/($in_use).*/&\t*/" | # append `*` to all in-use column -t # tabularize )" # Select all outputs to-use selected=( $(<<<"$formatted" \ fzf -m --cycle --header "Select outputs" | # select cut -d' ' -f1 # only output name ) ) # Abort if nothing was selected (( ${#selected[@]} )) || exit 1 # Regex to match either one of the selected outputs selected_regex="$(<<<"${selected[@]}" tr ' ' '|')" # Gather non-selected outputs non_selected=( $(<<<"$xrandr_q" \ grep connected | # list lines with output ({,dis}connected) grep -Ev "$selected_regex" | # filter out selected ones cut -d' ' -f1 # only output name ) ) # Choose primary primary="$(<<<"$formatted" \ grep -E "$selected_regex" | # use formatted for more information fzf --cycle --select-1 --header "Select primary" | cut -d' ' -f1 # name only )" [[ $primary ]] || exit 1 # Build the `xrandr` command xrandr_cmd=(xrandr) # Keep track of width to place the outputs side-by-side width=0 for out in "${selected[@]}"; do xrandr_cmd+=(--output "$out" --rotate normal) [[ $out != $primary ]] || xrandr_cmd+=(--primary) all_res="$(<<<"$xrandr_q" sed -En " # all resolution lines for selected output (+ junk lines first and last) /$out/,/^[^ ]/ { # on lines that contain a resolution /^ / { # only print the resolution s/^ *([^ ]*).*/\1/p } }" )" res="$(<<<"$all_res" fzf --cycle --header "Select resolution for $out")" [[ $res ]] || exit 1 xrandr_cmd+=(--mode "$res") rates="$(<<<"$xrandr_q" sed -En " # all resolution lines for selected output /$out/,/^[^ ]/ { # on line with the selected resolution /^ *$res/ { # remove resolution and whitespace s/^ *$res *// # fix formatting when preferred mode is not the current one s/ \+/+/ # put each rate on a new line s/ +/\n/g p } }" )" rate="$(<<<"$rates" fzf --cycle --header "Select refresh rate for $out")" [[ $rate ]] || exit 1 rate="${rate%%[*+]*}" # remove current and preferred mode indicator xrandr_cmd+=(--rate "$rate") xrandr_cmd+=(--pos "${width}x0") : "$((width+=${res%%x*}))" done for out in "${non_selected[@]}"; do xrandr_cmd+=(--output "$out" --off) done # Print for debugging purposes and finally execute it printf "%s\n" "${xrandr_cmd[*]}" "${xrandr_cmd[@]}"