|
|
- #!/usr/bin/zsh
- #
- # Setup outputs with xrandr as a backend and dmenu as a frontend
- #
- # external commands: cat, cut, dmenu, grep, sed, xrandr
- # Released under GPLv2
- #
-
- typeset XRANDR_TXT # readonly; stdout of running xrandr without any options
- typeset -A OUTPUT_CONFIGURED # key=connected output name
- typeset -a DISCONNECT_OUTPUTS
- typeset OPT_MODE='auto'
- typeset OPT_ROTATION='normal'
- typeset -ir EXIT_CLEAN=7
- typeset -ir ERR_BAD_ARGS=257
- typeset -ir ERR_BAD_SELECTION=258
- typeset -ir ERR_NO_OPTIONS=259
-
- if ! command -v cat &>/dev/null; then
- echo 'coreutils seem to be missing. You'\''re gonna have a bad time.' >&2
- exit 255
- elif ! command -v grep &>/dev/null; then
- echo 'grep seems to be missing. You'\''re gonna have a bad time.' >&2
- exit 255
- elif ! command -v xrandr &>/dev/null; then
- echo 'Ran xrandr-dmenu without xrandr? You'\''re gonna have a bad time.' >&2
- exit 255
- elif ! command -v dmenu &>/dev/null; then
- echo 'Ran xrandr-dmenu without dmenu? You'\''re gonna have a bad time.' >&2
- exit 255
- elif ! xset q &>/dev/null; then
- echo 'Woah there, cowboy! You need to run this from inside X!' >&2
- exit 1
- fi
-
- XRANDR_TXT="$(xrandr)" || exit $?
-
- function () {
- typeset opt
- typeset output
- typeset help_msg="usage: xrandr-dmenu [options]
- Setup X outputs position, mode, etc
- Input 'exit' or press 'ESC' at any point to cancel.
-
- Options:
- -m Select mode for outputs
- -M Use current mode for outputs
- -r Select rotation for outputs
- -R Use current rotation for outputs"
-
- while getopts mMrRh opt; do
- case "${opt}" in
- ('m') OPT_MODE='change' ;;
- ('M') OPT_MODE='no_change' ;;
- ('r') OPT_ROTATION='change' ;;
- ('R') OPT_ROTATION='no_change' ;;
- ('h')
- echo "${help_msg}"
- exit 0
- ;;
- ('?')
- echo "${help_msg}"
- exit 1
- ;;
- esac
- done
-
- for output in $(grep ' connected' <<< "${XRANDR_TXT}" | cut -d ' ' -f 1); do
- OUTPUT_CONFIGURED[${output}]='false'
- done
- for output ($(grep ' disconnected' <<< "${XRANDR_TXT}" | cut -d ' ' -f 1)) {
- DISCONNECTED_OUTPUTS+=("${output}")
- }
- } "$@"
-
- typeset -r XRANDR_TXT
- typeset -r OPT_MODE
- typeset -r OPT_ROTATION
- typeset -r DISCONNECTED_OUTPUTS
-
- function main() {
- typeset prompt
- typeset menu
- typeset output
- typeset mode
- typeset rotation
- typeset position
- typeset xrandr_cmd
-
- # set primary output
- prompt='Select primary output:'
- output="$(menu_select "${prompt}" ${(k)=OUTPUT_CONFIGURED})"
-
- [ "$output" = "eDP1" ] && bail 1
-
- position='--primary'
- mode="$(select_mode ${output})" || bail $?
- rotation="$(select_rotation ${output})" || bail $?
-
- OUTPUT_CONFIGURED[${output}]='true'
- xrandr_cmd="xrandr --output ${output} ${position} ${rotation} ${mode}"
-
- # set additional outputs
- prompt='Select next output:'
- while ! all_outputs_configured; do
- menu="$(list_unconfigured_outputs)"
- output="$(menu_select ${prompt} ${=menu})" || bail $?
-
- position="$(select_position ${output})" || bail $?
- if [[ "${position}" != '--off' ]]; then
- mode="$(select_mode ${output})" || bail $?
- rotation="$(select_rotation ${output})" || bail $?
- fi
-
- OUTPUT_CONFIGURED[${output}]='true'
- xrandr_cmd+=" --output ${output} ${position} ${rotation} ${mode}"
- done
-
- if [ "$(pidof x11vnc)" ]; then
- killvnc="$(printf "no\nyes" | dmenu -p "Would you like to kill existing vnc display?")"
- if [ "$killvnc" = "yes" ]; then
- xrandr --output HDMI2 --off
- killall -9 x11vnc
- vnc="$(printf "no\nyes" | dmenu -p "Would you like to connect to your ipad?")"
- fi
- else
- vnc="$(printf "no\nyes" | dmenu -p "Would you like to connect to your ipad?")"
- fi
-
- if [ "$vnc" = "yes" ]; then
- $HOME/.local/bin/ipad_xrandr.sh
- pidof x11vnc && notify-send -t 60000 -a " Network info" "$(echo -e "wlan0: $wlan\neth0: $eth\nRunning on port 5900")"
- fi
-
- # forcibly '--off' disconnected outputs
- for output in ${DISCONNECTED_OUTPUTS}; do
- xrandr_cmd+=" --output ${output} --off"
- done
-
- # do the deed
- if ! ${=xrandr_cmd}; then
- echo "Failed to execute xrandr command:\n${xrandr_cmd}"
- bail 255
- fi
- }
-
- ################################################################################
- # Uses dmenu to select the position of a given output relative to an already
- # configured output. --same-as and --off are considered a position.
- # Prints in the form of xrandr option (eg, '--right-of DP1') to stdout
- # Global variables:
- # ERR_BAD_ARG
- # Arguments:
- # $1=name of output to configure
- # Returns:
- # ERR_BAD_ARG for no $1
- ################################################################################
- function select_position() {
- [[ -z $1 ]] && return "${ERR_BAD_ARG}"
-
- typeset output
- typeset prompt
- typeset -a menu
- typeset anchor
- typeset position
- typeset selection
- output="$1"
- prompt="Select position of ${output}:"
-
- for anchor in $(list_configured_outputs); do
- for position in 'left of' 'right of' 'above' 'below' 'same as'; do
- menu+=("${position} ${anchor}")
- done
- done
- menu+=('off')
- selection="$(menu_select "${prompt}" ${menu})" || return $?
-
- case "${selection[(w)1]}" in
- (left|right|same) echo "--${selection/ /-}" ;;
- (above|below|mirror|off) echo "--${selection}" ;;
- esac
- }
-
- ################################################################################
- # Uses dmenu to display the detected mode options for a given output and lets
- # the user select a mode to use. Prints choice in xrandr option format
- # (eg, '--mode 800x600' or '--auto') to stdout
- # Global variables:
- # XRANDR_TXT
- # OPT_MODE
- # ERR_BAD_ARGS
- # Arguments:
- # $1 - name of which output we are working with
- # Returns:
- # ERR_BAD_ARGS
- ################################################################################
- function select_mode() {
- [[ -z $1 ]] && return "${ERR_BAD_ARGS}"
-
- typeset output
- typeset prompt
- typeset menu
- typeset selection
- output="$1"
- prompt="Select mode for ${output}:"
-
-
- if [[ "${OPT_MODE}" == 'auto' ]]; then
- echo '--auto'
- elif [[ "${OPT_MODE}" == 'no_change' ]]; then
- echo ''
- else
- # TODO: make this not ugly. A better sed should negate the need for cut/grep
- menu="$(echo \"${XRANDR_TXT}\" \
- | sed -n '/^'${output}' /,/^[^ ]/ s/ * //p' \
- | cut -d ' ' -f 1 \
- | grep x \
- | cat <(echo auto) -)"
-
- selection="$(menu_select "${prompt}" ${=menu})" || return $?
-
- if [[ 'auto' == "${selection}" ]]; then
- echo '--auto'
- else
- echo "--mode ${selection}"
- fi
- fi
- }
-
- function select_rotation() {
- [[ -z $1 ]] && return "${ERR_BAD_ARGS}"
-
- typeset menu
- typeset prompt
- prompt="Select rotation of $1:"
- menu=('normal' 'inverted' 'left' 'right')
-
- if [[ "${OPT_ROTATION}" == 'normal' ]]; then
- echo '--rotate normal'
- elif [[ "${OPT_ROTATION}" == 'no_change' ]]; then
- echo ''
- else
- echo -n "--rotate ${selection}"
- menu_select "${prompt}" ${menu} || return $?
- fi
- }
-
- function menu_select() {
- [[ -z "$2" ]] && return ${ERR_BAD_ARGS}
-
- typeset selection
- typeset prompt
- typeset -a menu
-
- prompt="$1"
- shift
- menu=($*)
-
- if [[ ${#menu} == 1 ]]; then
- echo "${menu}"
- else
- while [[ -z "${menu[(r)${selection}]}" ]]; do
- echo "${(F)menu}" | dmenu -p "${prompt}" | read selection
- [[ "${(L)selection}" == 'exit' ]] || [[ -z "${selection}" ]] \
- && return ${EXIT_CLEAN}
- done
- echo "${selection}"
- fi
- }
-
- function list_configured_outputs() {
- typeset -a list
- typeset output
-
- for output in ${(k)OUTPUT_CONFIGURED}; do
- ${OUTPUT_CONFIGURED[$output]} && list+=("${output}")
- done
- echo "${(F)list}"
- }
-
- function list_unconfigured_outputs() {
- typeset -a list
- typeset output
-
- for output in ${(k)OUTPUT_CONFIGURED}; do
- ${OUTPUT_CONFIGURED[$output]} || list+=("${output}")
- done
- echo "${(F)list}"
- }
-
- function all_outputs_configured() {
- typeset config
-
- for config in ${OUTPUT_CONFIGURED}; do
- $config || return 257
- done
-
- return 0
- }
-
- function bail() {
- [[ "$1" == ${EXIT_CLEAN} ]] && exit 0 || exit "$1"
- }
-
- main
-
-
|