Another copy of my dotfiles. Because I don't completely trust GitHub.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

307 lines
7.8 KiB

  1. #!/usr/bin/zsh
  2. #
  3. # Setup outputs with xrandr as a backend and dmenu as a frontend
  4. #
  5. # external commands: cat, cut, dmenu, grep, sed, xrandr
  6. # Released under GPLv2
  7. #
  8. typeset XRANDR_TXT # readonly; stdout of running xrandr without any options
  9. typeset -A OUTPUT_CONFIGURED # key=connected output name
  10. typeset -a DISCONNECT_OUTPUTS
  11. typeset OPT_MODE='auto'
  12. typeset OPT_ROTATION='normal'
  13. typeset -ir EXIT_CLEAN=7
  14. typeset -ir ERR_BAD_ARGS=257
  15. typeset -ir ERR_BAD_SELECTION=258
  16. typeset -ir ERR_NO_OPTIONS=259
  17. if ! command -v cat &>/dev/null; then
  18. echo 'coreutils seem to be missing. You'\''re gonna have a bad time.' >&2
  19. exit 255
  20. elif ! command -v grep &>/dev/null; then
  21. echo 'grep seems to be missing. You'\''re gonna have a bad time.' >&2
  22. exit 255
  23. elif ! command -v xrandr &>/dev/null; then
  24. echo 'Ran xrandr-dmenu without xrandr? You'\''re gonna have a bad time.' >&2
  25. exit 255
  26. elif ! command -v dmenu &>/dev/null; then
  27. echo 'Ran xrandr-dmenu without dmenu? You'\''re gonna have a bad time.' >&2
  28. exit 255
  29. elif ! xset q &>/dev/null; then
  30. echo 'Woah there, cowboy! You need to run this from inside X!' >&2
  31. exit 1
  32. fi
  33. XRANDR_TXT="$(xrandr)" || exit $?
  34. function () {
  35. typeset opt
  36. typeset output
  37. typeset help_msg="usage: xrandr-dmenu [options]
  38. Setup X outputs position, mode, etc
  39. Input 'exit' or press 'ESC' at any point to cancel.
  40. Options:
  41. -m Select mode for outputs
  42. -M Use current mode for outputs
  43. -r Select rotation for outputs
  44. -R Use current rotation for outputs"
  45. while getopts mMrRh opt; do
  46. case "${opt}" in
  47. ('m') OPT_MODE='change' ;;
  48. ('M') OPT_MODE='no_change' ;;
  49. ('r') OPT_ROTATION='change' ;;
  50. ('R') OPT_ROTATION='no_change' ;;
  51. ('h')
  52. echo "${help_msg}"
  53. exit 0
  54. ;;
  55. ('?')
  56. echo "${help_msg}"
  57. exit 1
  58. ;;
  59. esac
  60. done
  61. for output in $(grep ' connected' <<< "${XRANDR_TXT}" | cut -d ' ' -f 1); do
  62. OUTPUT_CONFIGURED[${output}]='false'
  63. done
  64. for output ($(grep ' disconnected' <<< "${XRANDR_TXT}" | cut -d ' ' -f 1)) {
  65. DISCONNECTED_OUTPUTS+=("${output}")
  66. }
  67. } "$@"
  68. typeset -r XRANDR_TXT
  69. typeset -r OPT_MODE
  70. typeset -r OPT_ROTATION
  71. typeset -r DISCONNECTED_OUTPUTS
  72. function main() {
  73. typeset prompt
  74. typeset menu
  75. typeset output
  76. typeset mode
  77. typeset rotation
  78. typeset position
  79. typeset xrandr_cmd
  80. # set primary output
  81. prompt='Select primary output:'
  82. output="$(menu_select "${prompt}" ${(k)=OUTPUT_CONFIGURED})"
  83. [ "$output" = "eDP1" ] && bail 1
  84. position='--primary'
  85. mode="$(select_mode ${output})" || bail $?
  86. rotation="$(select_rotation ${output})" || bail $?
  87. OUTPUT_CONFIGURED[${output}]='true'
  88. xrandr_cmd="xrandr --output ${output} ${position} ${rotation} ${mode}"
  89. # set additional outputs
  90. prompt='Select next output:'
  91. while ! all_outputs_configured; do
  92. menu="$(list_unconfigured_outputs)"
  93. output="$(menu_select ${prompt} ${=menu})" || bail $?
  94. position="$(select_position ${output})" || bail $?
  95. if [[ "${position}" != '--off' ]]; then
  96. mode="$(select_mode ${output})" || bail $?
  97. rotation="$(select_rotation ${output})" || bail $?
  98. fi
  99. OUTPUT_CONFIGURED[${output}]='true'
  100. xrandr_cmd+=" --output ${output} ${position} ${rotation} ${mode}"
  101. done
  102. if [ "$(pidof x11vnc)" ]; then
  103. killvnc="$(printf "no\nyes" | dmenu -p "Would you like to kill existing vnc display?")"
  104. if [ "$killvnc" = "yes" ]; then
  105. xrandr --output HDMI2 --off
  106. killall -9 x11vnc
  107. vnc="$(printf "no\nyes" | dmenu -p "Would you like to connect to your ipad?")"
  108. fi
  109. else
  110. vnc="$(printf "no\nyes" | dmenu -p "Would you like to connect to your ipad?")"
  111. fi
  112. if [ "$vnc" = "yes" ]; then
  113. $HOME/.local/bin/ipad_xrandr.sh
  114. pidof x11vnc && notify-send -t 60000 -a " Network info" "$(echo -e "wlan0: $wlan\neth0: $eth\nRunning on port 5900")"
  115. fi
  116. # forcibly '--off' disconnected outputs
  117. for output in ${DISCONNECTED_OUTPUTS}; do
  118. xrandr_cmd+=" --output ${output} --off"
  119. done
  120. # do the deed
  121. if ! ${=xrandr_cmd}; then
  122. echo "Failed to execute xrandr command:\n${xrandr_cmd}"
  123. bail 255
  124. fi
  125. }
  126. ################################################################################
  127. # Uses dmenu to select the position of a given output relative to an already
  128. # configured output. --same-as and --off are considered a position.
  129. # Prints in the form of xrandr option (eg, '--right-of DP1') to stdout
  130. # Global variables:
  131. # ERR_BAD_ARG
  132. # Arguments:
  133. # $1=name of output to configure
  134. # Returns:
  135. # ERR_BAD_ARG for no $1
  136. ################################################################################
  137. function select_position() {
  138. [[ -z $1 ]] && return "${ERR_BAD_ARG}"
  139. typeset output
  140. typeset prompt
  141. typeset -a menu
  142. typeset anchor
  143. typeset position
  144. typeset selection
  145. output="$1"
  146. prompt="Select position of ${output}:"
  147. for anchor in $(list_configured_outputs); do
  148. for position in 'left of' 'right of' 'above' 'below' 'same as'; do
  149. menu+=("${position} ${anchor}")
  150. done
  151. done
  152. menu+=('off')
  153. selection="$(menu_select "${prompt}" ${menu})" || return $?
  154. case "${selection[(w)1]}" in
  155. (left|right|same) echo "--${selection/ /-}" ;;
  156. (above|below|mirror|off) echo "--${selection}" ;;
  157. esac
  158. }
  159. ################################################################################
  160. # Uses dmenu to display the detected mode options for a given output and lets
  161. # the user select a mode to use. Prints choice in xrandr option format
  162. # (eg, '--mode 800x600' or '--auto') to stdout
  163. # Global variables:
  164. # XRANDR_TXT
  165. # OPT_MODE
  166. # ERR_BAD_ARGS
  167. # Arguments:
  168. # $1 - name of which output we are working with
  169. # Returns:
  170. # ERR_BAD_ARGS
  171. ################################################################################
  172. function select_mode() {
  173. [[ -z $1 ]] && return "${ERR_BAD_ARGS}"
  174. typeset output
  175. typeset prompt
  176. typeset menu
  177. typeset selection
  178. output="$1"
  179. prompt="Select mode for ${output}:"
  180. if [[ "${OPT_MODE}" == 'auto' ]]; then
  181. echo '--auto'
  182. elif [[ "${OPT_MODE}" == 'no_change' ]]; then
  183. echo ''
  184. else
  185. # TODO: make this not ugly. A better sed should negate the need for cut/grep
  186. menu="$(echo \"${XRANDR_TXT}\" \
  187. | sed -n '/^'${output}' /,/^[^ ]/ s/ * //p' \
  188. | cut -d ' ' -f 1 \
  189. | grep x \
  190. | cat <(echo auto) -)"
  191. selection="$(menu_select "${prompt}" ${=menu})" || return $?
  192. if [[ 'auto' == "${selection}" ]]; then
  193. echo '--auto'
  194. else
  195. echo "--mode ${selection}"
  196. fi
  197. fi
  198. }
  199. function select_rotation() {
  200. [[ -z $1 ]] && return "${ERR_BAD_ARGS}"
  201. typeset menu
  202. typeset prompt
  203. prompt="Select rotation of $1:"
  204. menu=('normal' 'inverted' 'left' 'right')
  205. if [[ "${OPT_ROTATION}" == 'normal' ]]; then
  206. echo '--rotate normal'
  207. elif [[ "${OPT_ROTATION}" == 'no_change' ]]; then
  208. echo ''
  209. else
  210. echo -n "--rotate ${selection}"
  211. menu_select "${prompt}" ${menu} || return $?
  212. fi
  213. }
  214. function menu_select() {
  215. [[ -z "$2" ]] && return ${ERR_BAD_ARGS}
  216. typeset selection
  217. typeset prompt
  218. typeset -a menu
  219. prompt="$1"
  220. shift
  221. menu=($*)
  222. if [[ ${#menu} == 1 ]]; then
  223. echo "${menu}"
  224. else
  225. while [[ -z "${menu[(r)${selection}]}" ]]; do
  226. echo "${(F)menu}" | dmenu -p "${prompt}" | read selection
  227. [[ "${(L)selection}" == 'exit' ]] || [[ -z "${selection}" ]] \
  228. && return ${EXIT_CLEAN}
  229. done
  230. echo "${selection}"
  231. fi
  232. }
  233. function list_configured_outputs() {
  234. typeset -a list
  235. typeset output
  236. for output in ${(k)OUTPUT_CONFIGURED}; do
  237. ${OUTPUT_CONFIGURED[$output]} && list+=("${output}")
  238. done
  239. echo "${(F)list}"
  240. }
  241. function list_unconfigured_outputs() {
  242. typeset -a list
  243. typeset output
  244. for output in ${(k)OUTPUT_CONFIGURED}; do
  245. ${OUTPUT_CONFIGURED[$output]} || list+=("${output}")
  246. done
  247. echo "${(F)list}"
  248. }
  249. function all_outputs_configured() {
  250. typeset config
  251. for config in ${OUTPUT_CONFIGURED}; do
  252. $config || return 257
  253. done
  254. return 0
  255. }
  256. function bail() {
  257. [[ "$1" == ${EXIT_CLEAN} ]] && exit 0 || exit "$1"
  258. }
  259. main